ODP.NET UPDATE ... RETURNING INTO ...多行,参数类型

时间:2012-04-12 13:26:49

标签: .net oracle odp.net

我想知道下列行动方案是否可行:

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

我想到的第一件事就是执行(ExecuteNonQuery)UPDATE some_table SET status ='locked'WHERERE rownum = 1 RETURNING id INTO:locked_id with output parameter,给我锁定记录的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自动处理,是的,我在每次UPDATE期间使用事务 - 因此两个用户同时锁定记录的可能性很小 - 或者这就是我听到的(将尝试这样做,一旦我找到了如何更新...返回...多行)

2 个答案:

答案 0 :(得分:2)

最后,经过几个小时的搜索和玩代码后,我得出了以下结论(除了头痛):

我从

获得了我想要的组合
  1. 提示here,建议将UPDATE..RETURNING语句包装到匿名PL / SQL块中(以BEGIN开头,以END结束;) - 这没有解释,我仍然不知道究竟为什么行为不同
  2. 关于OracleCommand的Oracle文档中的
  3. 代码片段,特别是带有BULK COLLECT INTO的part about binding PL/SQL associative arrays(无法使简单的数组绑定工作..):
  4. 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
    }
    ...
    

    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 :(得分:1)

以下是有关此记录锁定方案的一些事项:

  1. 你怎么知道谁锁定了记录?我怀疑在某些时候你会想知道。
  2. 如果会话崩溃,记录如何解锁?当然,他们不会在时间结束前保持锁定状态?!?
  3. 如何处理同时锁定尝试?假设我开始一个事务来锁定记录#12345。我的应用程序看到该状态为“已解锁”(或NULL或其他),并立即执行UPDATE和COMMIT以“锁定”记录。与此同时,苏茜在隔壁的立方体做了同样的事情 - 读取数据库,看到它已解锁,并执行相同的更新和提交。现在好了 - 1)记录的状态是什么,2)真正把它锁定了,3)其后续的变化将被覆盖 - 我告诉你,它更好是苏西或我来了“在那里真的很讨厌这个!!!”
  4. 只需要几点思考。

    分享并享受。