2012-04-12 74 views
0

我想知道如果采取下述方案是可能的:ODP.NET UPDATE ... RETURNING INTO ...多行,参数类型

我实现一个简单的数据输入应用程序。每个可编辑记录驻留在一个表中。用户必须同时编辑多个记录(以减少数据输入时间)。我现在试图做的是在记录中实现某种锁定机制。

我想到的第一件事是执行(ExecuteNonQuery)UPDATE some_table SET status ='locked'WHERE rownum = 1返回ID INTO:locked_id与输出参数,给我锁定记录的ID。我可以执行另一个SELECT语句来读取我想要的任何其他信息,并以此ID为基础。

虽然上述方法似乎适用于单个锁定记录,但我不知道如何为多个返回的行执行此操作。 - 如果在上例中WHERE子句是“rownum < 4”而不是“rownum = 1”?

我的OracleParameter应该是什么样子? 当我指定我会返回Oracle参数是这样的(可与单排),

OracleParameter p = cmd.Parameters.Add("ID", OracleDbType.Int32, 10, 0, ParameterDirection.Output);

ORA-24369中给出。我尝试使用ArrayBindCount和数组作为参数的.Value属性,但无济于事。

另外,你是否发现锁定的整个特定方式有什么根本错误?

谢谢

编辑:一些澄清 - 1.有一个名为USER_NAME列,可容纳例如登录的用户名 - 我更新与锁定UPDATE 2.沿有另一列,我称之为locked_timestamp,持有当记录被锁的时间。可能会有一个后台进程在夜间运行,并将锁定的记录重置为“解锁” - 因为这种情况不会频繁发生,并且由于崩溃等情况而保持“锁定”的记录比例很小比较的编辑记录 3.并发更新都应该是由Oracle自动处理量,是的,我每次更新过程中使用的交易 - 因此几乎没有什么机会,一个记录被通过同时两个用户锁定 - 或者说是我听到的(会尝试了这一点,一旦我找到了如何更新... RETURNING ..多行)

回答

1

最后,搜索与代码玩了几个小时之后,我来到了以下结论(除头痛):

我得到了我想要使用组合从

  1. 暗示here,它建议将UPDATE..RETURNING语句包装成一个匿名的PL/SQL块(以BEGIN开始并以END结束;) - 这个没有解释,我仍然不知道为什么行为不同
  2. code snippet在Oracle文档中有关OracleCommand的具体介绍在part about binding PL/SQL associative arrays与BULK收进(不能让简单的阵列结合工作..):

try 
{ 
    conn.Open(); 
    transaction = conn.BeginTransaction(); 

    cmd = new OracleCommand(); 
    cmd.Connection = GetConnection(); 

    cmd.CommandText = 
     "BEGIN UPDATE some_table " + 
     "SET status = 'locked', " + 
     " locked_tstamp = SYSDATE, " + 
     " user_name = '" + user + "' " + 
     "WHERE rownum <= 4 " + 
     "RETURNING id BULK COLLECT INTO :id; END;"; 

    cmd.CommandType = CommandType.Text; 

    cmd.BindByName = true; 
    cmd.ArrayBindCount = 4; 

    p = new OracleParameter(); 
    p.ParameterName = "id"; 
    p.Direction = ParameterDirection.Output; 
    p.OracleDbType = OracleDbType.Int64; 
    p.Size = 4; 
    p.ArrayBindSize = new int[] { 10, 10, 10, 10 }; 
    p.CollectionType = OracleCollectionType.PLSQLAssociativeArray; 
    cmd.Parameters.Add(p); 

    int nRowsAffected = cmd.ExecuteNonQuery(); 

    // nRowsAffected is always -1 here 
    // we can check the number of "locked" rows only by counting elements in p.Value (which is returned as OracleDecimal[] here) 
    // note that the code also works if less than 4 rows are updated, with the exception of 0 rows 
    // in which case an exception is thrown - see below 
    ... 
} 
catch (Exception ex) 
{ 
    if (ex is OracleException && !String.IsNullOrEmpty(ex.Message) && ex.Message.Contains("ORA-22054")) // precision underflow (wth).. 
    { 
     Logger.Log.Info("0 rows fetched"); 
     transaction.Rollback(); 
    } 
    else 
    { 
     Logger.Log.Error("Something went wrong during Get : " + ex.Message); 
     ret = null; 
     transaction.Rollback(); 
    } 
} 
finally 
{ 
    // do disposals here 
} 
... 

+1

因为代码结束有点尴尬,我的主要结论是使用预编译的PL/SQL过程 - 获得的好处是SQL代码将被预先存储,它将更加结构化并且锁定逻辑将驻留在数据库中。该过程也可以编码以返回锁定记录的数量,以及规避可怕的ORA-22054异常 – 2012-04-18 15:05:34

1

这里有几件事情要考虑这方面的记录锁定方案:

  1. 你如何知道谁锁定了记录?我怀疑在某个时候你会想知道。
  2. 如果会话崩溃,如何记录得到解锁?当然,他们不会保持锁定,直到时间的结束?!?
  3. 如何同时锁定尝试处理?假设我开始一个事务来锁定记录#12345。我的应用程序看到,该状态是“解锁”(或NULL,或其他),并立即执行UPDATE和COMMIT来“锁定”记录。与此同时,Susie在隔壁的立方体上也做同样的事情 - 读取数据库,查看它的解锁状态,并执行相同的UPDATE和COMMIT。现在好了 - 1)什么是记录的状态,2)谁真正拥有它锁定,3),其后续变化将要被改写 - 我告诉你,它最好是苏茜还是我马上就要'在那里谈论真的很多!

只有几点思考。

分享和享受。

+0

谢谢你的答复。的确,因为实际上我的主要问题是如何在多行上执行UPDATE..RETURNING ..我省略了一些关于“我正在描述的锁定方案”的内容 - 我在底部编辑了问题的主体,回复到你提到的点 – 2012-04-18 07:15:03

+0

我不知道你的问题的确切答案,并且现在没有.Net方便,但你应该详细调查ORA-24369。 ORA-24369的描述是:“用RETURNING子句在DML statememt中接收数据的绑定句柄必须将它们的模式设置为DATA_AT_EXEC,并且必须使用OCIBindDynamic为这些绑定句柄注册回调函数”。因此,似乎需要将参数模式设置为DATA_AT_EXEC,并且需要提供回调函数。 – 2012-04-18 10:45:30