IDENTITY_INSERT ON
単独で設定しても同時実行性は排除されません。これにより、テーブルに排他ロックが設定されず、簡単なスキーマ安定性(Sch-S)ロックのみが設定されます。
理論的には、デフォルトの動作では、セッション1でこれを行うことができます。
BEGIN TRANSACTION;
-- 1
SET IDENTITY_INSERT dbo.tablename ON;
-- 2
INSERT dbo.tablename(id, etc) VALUES(100, 'foo'); -- next identity is now 101
-- 3
INSERT dbo.tablename(id, etc) VALUES(101, 'foo'); -- next identity is now 102
-- 4
SET IDENTITY_INSERT dbo.tablename OFF;
COMMIT TRANSACTION;
別のセッションでは、あなたはこれが可能ポイント1、2、3または4で、テーブルに行を挿入することができますように見える、2と3の間で起こるすべての挿入のために何が起こるかを除いて、良いことのように自動生成された値がトリガということです別のセッションでは、ステートメント2の結果に基づいています。そのため、101が生成され、ステートメント3は主キー違反で失敗します。これは、いくつかWAITFOR
のs を使用して設定およびテストするのがかなり簡単です。
-- session 1
-- DROP TABLE dbo.what;
CREATE TABLE dbo.what(id INT IDENTITY PRIMARY KEY);
GO
BEGIN TRANSACTION;
SET IDENTITY_INSERT dbo.what ON;
INSERT dbo.what(id) VALUES(32);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(33);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(34);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(35);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(36);
SET IDENTITY_INSERT dbo.what OFF;
COMMIT TRANSACTION;
そのバッチが開始したら、別のウィンドウでこのバッチを開始します。
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
セッション2では、1〜20の値のみを挿入する必要があります。ただし、基になるIDは手動挿入セッション1によって更新されているため、ある時点で、セッション2はセッション1が終了した場所を取得し、32、33、または34などを挿入します。これは可能ですが、次に、セッション1は次の挿入でPK違反で失敗します(どちらが勝つかはタイミングの問題である場合があります)。
これを回避する1つの方法はTABLOCK
、最初の挿入でを呼び出すことです。
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
これにより、アーカイブされた行の移動が完了するまで、他のユーザーがこのテーブルを挿入しようとする(実際には何もしない)ことがブロックされます。これは確かに、並行性を絞るが、これは、あなたが道であるたい仕事に遮断します。うまくいけば、これは他の人を常にブロックしているような頻繁な頻度で起こっていることではありません。
他のいくつかの回避策:
IDENTITY
生成された値を気にしないでください。誰も気にしない?元の値が非常に重要な場合は、(代理としてを使用UNIQUEIDENTIFIER
して別のテーブルで生成されるIDENTITY
場合があります)を使用します。
- アーカイブプロセスを変更して、「ソフト削除」を使用するようにします。この場合、何かが最初にアーカイブ済みとしてマークされ、アーカイブは後日まで永続化されません。次に、それらを元に戻そうとしているプロセスは、直接更新を実行して、ソフト削除フラグを修正するだけです。