Pengaturan IDENTITY_INSERT ON
sendiri tidak menghilangkan konkurensi - ini tidak menempatkan kunci eksklusif di atas meja, hanya kunci skema stabilitas (Sch-S) singkat.
Jadi apa yang secara teoritis bisa terjadi, di bawah perilaku default, adalah Anda bisa melakukan ini di sesi 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;
Di sesi lain, Anda bisa menyisipkan baris ke tabel pada poin 1, 2, 3 atau 4. Ini mungkin tampak seperti hal yang baik, kecuali apa yang terjadi untuk setiap sisipan yang terjadi antara 2 dan 3, adalah bahwa nilai yang dihasilkan secara otomatis dipicu oleh sesi lain didasarkan pada hasil pernyataan 2 - sehingga akan menghasilkan 101, dan kemudian pernyataan 3 akan gagal dengan pelanggaran kunci utama. Ini cukup sederhana untuk mengatur dan menguji diri Anda dengan beberapa 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;
Setelah kumpulan itu dimulai, mulai kumpulan ini di jendela lain:
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
Sesi 2 seharusnya hanya memasukkan nilai dari 1-20, kan? Kecuali itu karena identitas yang mendasarinya telah diperbarui oleh sesi manual sisipan 1 Anda, pada beberapa titik sesi 2 akan mengambil di mana sesi 1 tinggalkan, dan menyisipkan 32, atau 33, atau 34 dll. Ini akan diizinkan untuk melakukan ini, tetapi maka sesi 1 akan gagal pada sisipan berikutnya dengan pelanggaran PK (yang menang mungkin hanya masalah waktu).
Salah satu cara untuk mengatasinya adalah dengan memohon TABLOCK
pada sisipan pertama:
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Ini akan memblokir setiap pengguna lain yang mencoba menyisipkan (atau melakukan apa saja, sungguh) dengan tabel ini sampai Anda selesai memindahkan baris yang diarsipkan kembali. Ini mencekik konkurensi, tentu saja, tetapi ini adalah cara Anda ingin memblokir untuk bekerja. Dan mudah-mudahan ini bukan sesuatu yang terjadi pada tingkat yang sering terjadi di mana Anda memblokir orang lain sepanjang waktu.
Beberapa solusi lain:
- berhenti peduli tentang
IDENTITY
nilai yang dihasilkan. Siapa peduli? Gunakan a UNIQUEIDENTIFIER
(mungkin dihasilkan dalam tabel terpisah dengan IDENTITY
sebagai pengganti) jika nilai aslinya sangat penting.
- ubah proses arsip untuk menggunakan "hapus lunak" di mana sesuatu ditandai sebagai diarsipkan pada awalnya dan arsip tidak dibuat permanen sampai beberapa waktu kemudian. Maka proses apa pun yang mencoba untuk memindahkan mereka kembali hanya dapat melakukan pembaruan langsung dan memperbaiki bendera hapus lunak.