存在しない場合は同時に挿入


13

ストアドプロシージャへの挿入で並行性の問題が発生しています。手順の関連部分は次のとおりです。

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    insert into table1 (othervalue) values (@_othervalue)
    select @_id = Id from table1 where othervalue = @_othervalue
END

これらのストアドプロシージャを3つまたは4つ同時に実行すると、複数の挿入が発生することがあります。

私はこれを次のように修正することを計画しています:

insert into table1 (othervalue) 
    select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK) 
    where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )

select @_id = Id from table1 where othervalue = @_othervalue

問題は、SQLサーバーに重複せずに同時に挿入する方法ですか?私は一度だけ挿入するためにTOPを使用しなければならないという事実は私を混乱させます。


1
TOPを使用する必要はありません。SELECTステートメントからFROMテーブル参照を削除します。
エリック


@GSergあなたは正しいと思います。
クリス

回答:


7

serializableヒント付きのマージ文を使用できます。

merge table1 with (serializable) as T 
using (select @_othervalue as othervalue) as S
on T.othervalue = S.othervalue
when not matched then
  insert (othervalue) values (othervalue);

2つ以上の接続からアプローチをストレステストしましたか?
AK

2
@AlexKuznetsov-SOに関する別の質問のために少し前にやりました。SSMSで2つのタブを使用しました。最初にinsert ... where not exist ...パターンをテストしたところ、デッドロックとキー違反が発生する可能性があることがわかったため、updlockとserializableを使用する必要がありました。その後、マージステートメントをテストし、デッドロックが発生しないため、物事を少しうまく処理できると考えましたが、キー違反がないようにシリアライズ可能を使用する必要がありました。
ミカエルエリクソン

1
これは本当に素晴らしい答えです。
クリスマリシック

5

「othervalue」列に重複を作成したくない場合はunique constraint、その列にを作成することで作成できます。クエリは次のようになります。

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

クエリが重複する値を「othervalue」列に挿入しようとした場合、これはエラーを返します。


一意性制約が2行のタプルである場合、どのように機能しますか?
クリス

1
@Chris行にまたがる一意の制約はどのようにありますか?
アーロンバートランド

@Aaron私はおそらく用語をオフにしていますが、2つの行があり、それらを一緒に一意にする必要があります。私たちのスキーマで強制されているとは思わない。
クリス

2

@StanleyJohnsが推奨するような一意の制約を使用します。次に、INSERTステートメントの周りでBEGIN TRY END TRYを使用します。

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    BEGIN TRY
        insert into table1 (othervalue) values (@_othervalue)
        select @_id = Id from table1 where othervalue = @_othervalue        
    END TRY
    BEGIN CATCH
        select @_id = Id from table1 where othervalue = @_othervalue        
    END CATCH
END
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.