この文:
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つのレコードを同じバッチで送信することはありません。
本当に同時に複数のスレッドを実行する必要がある場合は、主キーで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));
IGNORE_DUP_KEYオプションに関するポールホワイトの詳細な説明 。ポールに感謝します。
テーブルの名前DeleteMe
は、IDをこのテーブルに蓄積し、これらのIDを持つ行を他の永続テーブルから定期的に削除することを示しています。本当ですか?
Trueの場合、DeleteMe
に重複したIDを許可できます。 id
に一意ではなく非一意のインデックスがあるだけです(id
はuniqueidentifier
であるため、このインデックスも非クラスター化することは理にかなっています)。
CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
)
CREATE NONCLUSTERED INDEX [IX_ID] ON [dbo].[DeleteMe]
(
[id] ASC
)
DeleteMe
を使用して別のMainTable
から行を削除する場合、id
のDeleteMe
が一意であるかどうかは関係ありません。
DELETE FROM MainTable
WHERE MainTable.ID IN (SELECT id FROM DeleteMe)
ところで、DeleteMe
に入力するクエリもはるかに単純になります。
INSERT INTO DeleteMe (id, Value)
SELECT #t.id, 'test'
FROM #t