web-dev-qa-db-ja.com

「重複キー」エラーを回避する方法は?

この文:

INSERT INTO deleteme
SELECT #t.id, 'test' FROM #t
LEFT JOIN  deleteme  ON deleteme.id = #t.id
WHERE deleteme.id IS NULL;

...並行シナリオ(つまり、複数のスレッドからdeletemeに存在しない同じキーを同時に挿入する場合)で、デフォルトのトランザクション分離レベルREAD COMMITTEDを使用すると、主キー違反で失敗します。

エラー:PRIMARY KEY制約「PK_DeleteMe」の違反。オブジェクト 'dbo.deleteme'に重複するキーを挿入することはできません。

それを防ぐ最善の方法は何ですか?

テーブルは次のようになります。

CREATE TABLE [dbo].[DeleteMe](
    [id] [uniqueidentifier] NOT NULL,
    [Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] PRIMARY KEY ([id] ASC));

更新

コメントから:

複数のセッションがあり、それらを区別するためのなんらかのセッションキーがなくても、同じ永続テーブルを使用して、明らかにどこかから同じIDをプルできるのはなぜですか?重複エラーがない場合でも、どの行がどのセッションに属しているかをどのようにして知るのでしょうか。

これは、このテーブルにデータを入力する外部サービスによって呼び出されるストアドプロシージャです。サービスはレコードIDを生成し、同じデータを2回送信しないという保証はありません。または、同じデータを同時に送信します。

データベースコードは、すでに存在する場合、同じIDのすべてのレコードを破棄することになっています。サービスは、同じIDを持つ2つのレコードを同じバッチで送信することはありません。

5
Andrew Savinykh

本当に同時に複数のスレッドを実行する必要がある場合は、主キーでignore_dup_keyオプションを有効にすることができます。

これにより、挿入によって重複キー違反が発生した場合に、エラーではなく警告が表示されます。しかし、失敗する代わりに、挿入された場合に一意性違反を引き起こす行を破棄します。

CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] 
PRIMARY KEY ([id] ASC) 
WITH (IGNORE_DUP_KEY = ON));

sqlfiddleの例

IGNORE_DUP_KEYオプションに関するポールホワイトの詳細な説明 。ポールに感謝します。

10
Aaron

テーブルの名前DeleteMeは、IDをこのテーブルに蓄積し、これらのIDを持つ行を他の永続テーブルから定期的に削除することを示しています。本当ですか?

Trueの場合、DeleteMeに重複したIDを許可できます。 idに一意ではなく非一意のインデックスがあるだけです(iduniqueidentifierであるため、このインデックスも非クラスター化することは理にかなっています)。

CREATE TABLE [dbo].[DeleteMe](
    [id] [uniqueidentifier] NOT NULL,
    [Value] [varchar](max) NULL,
)

CREATE NONCLUSTERED INDEX [IX_ID] ON [dbo].[DeleteMe]
(
    [id] ASC
)

DeleteMeを使用して別のMainTableから行を削除する場合、idDeleteMeが一意であるかどうかは関係ありません。

DELETE FROM MainTable
WHERE MainTable.ID IN (SELECT id FROM DeleteMe)

ところで、DeleteMeに入力するクエリもはるかに単純になります。

INSERT INTO DeleteMe (id, Value)
SELECT #t.id, 'test' 
FROM #t
0