1

(他の種類の番号の中でも)インシデント番号を使用するアプリケーションがあります。これらの番号は、カウンターの現在の値を含む「Number_Setup」と呼ばれるテーブルに格納されます。

アプリが新しいインシデントを生成すると、number_setupテーブルが作成され、必要な番号カウンター行が取得されます(カウンターは毎日、毎週などにリセットでき、intとして保存されます)。次に、カウンターをインクリメントし、新しい値で行を更新します。

アプリケーションはマルチユーザーです(一度に約100人のユーザー、および数百のインシデントレコードを実行して取得し、それぞれのインシデント番号を要求するSQLジョブ)。インシデントテーブルには、重複してはならない重複するインシデント番号がいくつかあります。

ストアドプロシージャは、次のカウンタを取得するために使用されます。

SELECT @Counter = counter、@ ShareId = share_id、@ Id = id
FROM Number_Setup
WHERE LinkTo_ID = @ LinkToId
AND Counter_Type ='I'

IF isnull(@ ShareId、0)> 0
始める
    -親カウンターを使用します
    SELECT @Counter = counter、@ ID = id
    FROM Number_Setup
    WHERE Id = @ ShareID
終わり

SELECT @NewCounter = @Counter + 1

UPDATE Number_Setup SET Counter = @NewCounter
WHERE id = @ Id

そのブロックをトランザクションで囲みましたが、まだ共有ロックがあると思うので、問題が100%修正されるかどうかは完全にはわかりません。そのため、とにかくカウンターを読み取ることができます。

おそらく、updateステートメントでカウンターが更新されていないことを確認できます

UPDATE Number_Setup SET Counter = @NewCounter
WHERE Counter = @Counter
@@ ERROR=0かつ@@ROWCOUNT>0の場合
    トランザクションのコミット
そうしないと
    ロールバックトランザクション

これは、金融アプリなどの請求書番号でよくある問題だと確信しています。
ロジックをコードに入れて、そのレベルでロックを使用することもできません。私もHOLDLOCKでロックしましたが、そのアプリケーションがわかりません。2つのSELECTステートメントに配置する必要がありますか?

重複が作成されないようにするにはどうすればよいですか?

4

4 に答える 4

4

秘訣は、カウンターの更新を行い、単一のアトミック操作で読み取ることです。

UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter 
WHERE id=@Id;

ただし、これは新しいカウンターを@NewCounterに割り当てませんが、代わりに結果セットとしてクライアントに返します。割り当てる必要がある場合は、中間テーブル変数を使用して新しいカウンターINTOを出力します。

declare @NewCounter int;
declare @tabCounter table (NewCounter int);
UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter)
WHERE id=@Id
SELECT @NewCounter = NewCounter FROM @tabCounter;

これにより、Counterインクリメントをアトミックにする問題が解決されます。LinkTo_Idとshare_idは最初の選択後に更新できるため、手順にはまだ他の競合状態があります。これにより、間違ったリンク先アイテムのカウンターをインクリメントできますが、このコードサンプルだけでは解決できません。 shared_idやLinkTo_Id、あるいはその両方を実際に更新するコード。

ところで、一貫した大文字と小文字を区別してフィールドに名前を付ける習慣を身に付ける必要があります。それらに一貫した名前付けられている場合は、T-SQLコードで完全一致の大文字と小文字を使用する必要があります。大文字と小文字を区別しない照合サーバーを使用しているという理由だけでスクリプトが正常に実行されるようになりました。大文字と小文字を区別する照合サーバーにデプロイし、スクリプトがフィールド/テーブル名の正確な大文字と小文字と一致しない場合、エラーが大量に発生します。

于 2009-10-08T23:27:05.893 に答える
0

一意の識別子として自動インクリメントの代わりにGUIDを使用してみましたか?

于 2009-10-08T22:44:30.807 に答える
0

複数のレコードを取得するジョブを変更できる場合は、カウンターが ID 列になるように考え方を変更します。次に、次のレコードを取得すると、挿入を実行してテーブルの @@identity を取得できます。これにより、最大の数が確実に得られます。ID をリセットする場合は、テーブルを更新するだけでなく、カウンターをリセットするために dbccReseed を実行する必要もあります。唯一の問題は、ID のグループを取得するために、SQL ジョブの一部として 100 回程度の挿入を行う必要があることです。これはオーバーヘッドが大きすぎる可能性がありますが、ID 列を使用すると、一意の番号を確実に取得できます。

于 2009-10-08T22:53:28.713 に答える
0

何かが足りないかもしれませんが、ほとんどのデータベースですでに解決されている技術を再発明しようとしているようです。

Number_Setup テーブルの「Counter」列を読み取って更新する代わりに、カウンターに自動インクリメント主キーを使用してみませんか? 主キーの値が重複することはありません。

于 2009-10-08T22:55:06.707 に答える