A configuração IDENTITY_INSERT ON
por si só não elimina a simultaneidade - isso não coloca nenhum bloqueio exclusivo na tabela, apenas um bloqueio breve de estabilidade do esquema (Sch-S).
Então, o que poderia teoricamente acontecer, sob o comportamento padrão, é que você poderia fazer isso na sessão 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;
Em outra sessão, você pode inserir linhas na tabela nos pontos 1, 2, 3 ou 4. Isso pode parecer uma coisa boa, exceto o que acontece com qualquer inserção que ocorre entre 2 e 3, é que o valor gerado automaticamente disparou em outra sessão é baseada nos resultados da instrução 2 - portanto, ela gerará uma 101 e, em seguida, a instrução 3 falhará com uma violação de chave primária. Isso é bastante simples de configurar e testar você mesmo com alguns 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;
Depois que o lote iniciar, inicie-o em outra janela:
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
A sessão 2 só deve inserir valores de 1 a 20, certo? Exceto que, como a identidade subjacente foi atualizada pelo seu manual insere a sessão 1, em algum momento a sessão 2 continuará onde a sessão 1 parou e inserirá 32, 33 ou 34 etc. Isso será permitido, mas a sessão 1 falhará na próxima inserção com uma violação de PK (qual deles vence pode ser apenas uma questão de tempo).
Uma maneira de solucionar isso é invocar um TABLOCK
na primeira inserção:
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Isso bloqueará qualquer outro usuário que tentar inserir (ou fazer alguma coisa, realmente) nesta tabela até que você termine de mover essas linhas arquivadas de volta. Isso limita a simultaneidade, com certeza, mas é dessa maneira que você deseja que o bloqueio funcione. E espero que isso não ocorra a um ritmo tão frequente em que você bloqueia outras pessoas o tempo todo.
Algumas outras soluções alternativas:
- pare de se preocupar com o
IDENTITY
valor gerado. Quem se importa? Use a UNIQUEIDENTIFIER
(talvez gerado em uma tabela separada com IDENTITY
um substituto) se o valor original for muito importante.
- altere o processo de arquivamento para usar uma "exclusão reversa" onde algo é marcado como arquivado inicialmente e o arquivamento não é tornado permanente até uma data posterior. Qualquer processo que esteja tentando movê-los de volta pode simplesmente executar uma atualização direta e corrigir o sinalizador de exclusão suave.