0

私は次の行動計画が可能かどうか疑問に思いました:

簡単なデータ入力アプリケーションを実装しています。編集可能な各レコードはテーブルにあります。ユーザーは同時に複数のレコードを編集する必要があります(データ入力時間を短縮するため)。私が今やろうとしていることは、レコードにある種のロックメカニズムを実装することです。

私が最初に考えたのは、UPDATE some_table SET status ='locked' WHERE rownum = 1 RETURNING id INTO:locked_idを出力パラメーターとともに実行して(ExecuteNonQuery)、ロックされたレコードのIDを取得することでした。このIDに基づいて、別のSELECTステートメントを実行して、必要な他の情報を読み取ることができます。

上記のアプローチは単一のロックされたレコードに対して機能するように見えますが、返された複数の行に対してこれを行う方法は実際にはわかりません。-上記の例で、WHERE句が「rownum =1」ではなく「rownum<4」だった場合はどうなりますか?

私のOracleParameterはどのようになりますか?このようにreturnoracleパラメーターを指定すると(単一の行で機能します)、

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人のユーザーによって同時にロックされる可能性はほとんどありません。聞こえます(これを試してみます、

4

2 に答える 2

2

最後に、何時間もコードを検索して遊んだ後、私は次の結論に達しました(頭痛は別として):

組み合わせて欲しいものを手に入れました

  1. ここでのヒントは、UPDATE..RETURNINGステートメントを匿名のPL / SQLブロックにラップすることを提案しました(BEGINで始まり、ENDで終わります;)-これは説明なしで行われ、動作が異なる理由はまだ正確にはわかりません
  2. OracleCommandに関するOracleドキュメントのコードスニペット、特にPL/SQL連想配列をBULKCOLLECTINTOにバインドする部分(単純な配列バインドを機能させることができませんでした):

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
}
...

于 2012-04-18T15:00:08.783 に答える
1

このレコードロックスキームに関して考慮すべきことがいくつかあります。

  1. 誰がレコードをロックしたかをどうやって知るのですか?いつかあなたが知りたいと思うでしょう。
  2. セッションがクラッシュした場合、レコードはどのようにロック解除されますか?確かに彼らは時間の終わりまでロックされたままになることはないだろうか?!?
  3. 同時ロック試行はどのように処理されますか?レコード#12345をロックするトランザクションを開始するとします。私のアプリは、そのステータスが「ロック解除」(またはNULLなど)であることを確認し、すぐにUPDATEとCOMMITを実行してレコードを「ロック」します。一方、隣のキューブのSusieも同じことを行います。データベースを読み取り、ロックが解除されているかどうかを確認し、同じUPDATEとCOMMITを実行します。OK、1)レコードのステータスはどうなっているのか、2)誰が本当にロックしているのか、3)その後の変更は上書きされるのか、ということですが、スージーか私が来たほうがいいでしょう。 'これについて本当に大声で話してください!!!

熟考するためのほんの数点。

共有してお楽しみください。

于 2012-04-12T17:00:32.523 に答える