0

「sadsadsad」が存在するかどうかを確認し、存在しない場合はテーブルに挿入するこの TSQL コードがあります。

if not exists(select id from [ua_subset_composite] where ua = 'sadsadsad')
  begin
    insert into [ua_subset_composite]
    select 'sadsadsad',1,null,null,null,null
  end

私の懸念は、複数のスレッドが同時に実行される本番環境では、存在しない選択と挿入の間でレコードがすり抜けるという状況が発生する可能性があることです。

列に一意の制約を追加したくないので、一意性が保証されるようにこの SQL コードを改善できるかどうか疑問に思っています。

4

5 に答える 5

2

これに対処する 1 つの方法は、より高いレベルの分離 (ロック) を使用することです。ステートメント全体をトランザクションでラップし、より厳密な分離レベルを使用できます。

例えば:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION

   <your code here>

COMMIT TRANSACTION
于 2013-01-09T17:39:31.373 に答える
0

データベースにロック戦略を実装できます。悲観的な選択肢があります。

あなたがそれを終えるまであなたがあなたの独占的な使用のために記録をロックするとき。楽観的ロックよりもはるかに優れた整合性がありますが、デッドロックを回避するためにアプリケーションの設計に注意する必要があります。

または楽観的:

レコードを読み取る場所で、バージョン番号をメモし、レコードを書き戻す前にバージョンが変更されていないことを確認します。レコードを書き戻すときは、バージョンの更新をフィルタリングして、アトミックであることを確認します。(つまり、バージョンを確認してからディスクにレコードを書き込むまでの間に更新されていません)、1回のヒットでバージョンを更新します。

レコードがダーティである場合(つまり、バージョンが異なる場合)、トランザクションを中止し、ユーザーはそれを再開できます。

ソース

于 2013-01-09T17:30:20.600 に答える
0

を実行するときは、選択している範囲に、をselect配置します。updlockholdlock

begin transaction

if not exists(
    select id 
    from [ua_subset_composite] with (updlock, holdlock) 
    where ua = 'sadsadsad')
  begin
    insert into [ua_subset_composite]
    select 'sadsadsad',1,null,null,null,null
  end

commit

は、分離レベルholdlockに相当し、次のような効果があります。serializable

  • ステートメントは、変更されているが他のトランザクションによってまだコミットされていないデータを読み取ることはできません。

  • 現在のトランザクションが完了するまで、他のトランザクションは現在のトランザクションによって読み取られたデータを変更できません。

  • 他のトランザクションは、現在のトランザクションが完了するまで、現在のトランザクションのステートメントによって読み取られるキーの範囲内にあるキー値を持つ新しい行を挿入できません。

範囲ロックは、トランザクションで実行される各ステートメントの検索条件に一致するキー値の範囲に配置されます。これにより、他のトランザクションが、現在のトランザクションによって実行されるステートメントのいずれかに適格となる行を更新または挿入するのをブロックします。これは、トランザクション内のステートメントのいずれかが2回目に実行された場合、それらが同じ行のセットを読み取ることを意味します。範囲ロックは、トランザクションが完了するまで保持されます。これは、キーの全範囲をロックし、トランザクションが完了するまでロックを保持するため、分離レベルの中で最も制限があります。同時実行性が低いため、このオプションは必要な場合にのみ使用してください。

...に加えて、が必要です。追加することにより、別のプロセスが同じ範囲で同時に独自のステートメントを実行するのをupdlock防ぎます。holdlockupdlockselect with (updlock, holdlock)

于 2013-01-09T17:31:05.577 に答える
0

これが私がやったことです

insert into [ua_subset_composite]  WITH (TABLOCKX) (ua, os)
select @r, 1 
where not exists (select 1 from [ua_subset_composite] nolock where ua = @r

コードをテストするために、このコードを複数のウィンドウから同時に実行しました

declare @r nvarchar(30);
while(1=1)
begin

set @r =  convert(nvarchar(30),getdate(),21 )
insert into [ua_subset_test]  WITH (TABLOCKX) (ua, os)
select @r, 1 
where not exists (select 1 from [ua_subset_test] nolock where ua = @r

)
end
于 2013-01-10T13:32:03.123 に答える
-1

残念ながら、上記の答えはどれも正しくありません。開始する「ロック」ソリューションに注意してくださいBEGIN TRAN SELECT。はい、分離レベルが SERIALIZABLE の場合、SELECT他のプロセスが選択したデータを更新できないようにするロックを作成します。しかし、データが選択されていない場合はどうなるでしょうか? 何をロックしますか?

IOW、BEGIN TRAN競合状態を設定します。

/* spid */
 /* 1 */   SELECT ... -- returns no rows
 /* 2 */   SELECT ... -- returns no rows
 /* 1 */   INSERT ... -- whew! 
 /* 2 */   INSERT ... -- error

書き込む前に読み取る (たとえば、ユーザーにデータを表示する) には、特別なタイムスタンプ データ型があります。ただし、あなたの場合、それは単なる挿入です。アトミック トランザクション、つまり単一のステートメントを使用します。

insert into [ua_subset_composite] (column1, column2)
values ('sadsadsad', 1)
where not exists (
    select 1 from ua_subset_composite 
    where column1 = 'sadsadsad'
)

サーバーは、行が挿入されるかどうかを保証します。ロックは、方法を知っている人によって、可能な限り短時間で、必要な場所で行われます。:-)

一意制約を追加したくない

まあ、おそらくそうすべきでしょう。上記のコードは、一意でない値を追加しようとするのを防ぎ、エラー メッセージを回避します。一意の制約は、注意を怠った人が成功するのを防ぎます。

于 2013-01-10T05:01:52.727 に答える