La définition IDENTITY_INSERT ON
seule n'élimine pas la concurrence - cela ne place aucun verrou exclusif sur la table, seulement un bref verrou de stabilité de schéma (Sch-S).
Donc, ce qui pourrait théoriquement se produire, avec le comportement par défaut, c'est que vous pourriez le faire dans la session 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;
Dans une autre session, vous pouvez insérer des lignes dans le tableau aux points 1, 2, 3 ou 4. Cela peut sembler une bonne chose, sauf ce qui se passe pour toute insertion qui se produit entre 2 et 3, c'est que la valeur générée automatiquement déclenchée par une autre session est basée sur les résultats de l'instruction 2 - elle générera donc un 101, puis l'instruction 3 échouera avec une violation de clé primaire. C'est assez simple à configurer et à tester vous-même avec quelques 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;
Une fois ce lot démarré, démarrez ce lot dans une autre fenêtre:
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
La session 2 ne devrait jamais insérer que des valeurs de 1 à 20, non? Sauf que parce que l'identité sous-jacente a été mise à jour par votre session d'insertions manuelles 1, à un moment donné, la session 2 reprendra là où la session 1 s'est arrêtée, et insérera 32, ou 33, ou 34 etc. Il sera autorisé à le faire, mais alors la session 1 échouera à l'insertion suivante avec une violation de PK (que l'on gagne peut être juste une question de timing).
Une façon de contourner ce problème consiste à appeler un TABLOCK
sur la première insertion:
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Cela bloquera tout autre utilisateur essayant d'insérer (ou de faire quoi que ce soit, vraiment) avec ce tableau jusqu'à ce que vous ayez terminé de reculer ces lignes archivées. Cela limite la concurrence, bien sûr, mais c'est ainsi que vous souhaitez que le blocage fonctionne. Et j'espère que ce n'est pas quelque chose qui se produit à un rythme si fréquent où vous bloquez d'autres personnes tout le temps.
Quelques autres solutions:
- arrêtez de vous soucier de la
IDENTITY
valeur générée. On s'en fout? Utilisez un UNIQUEIDENTIFIER
(peut-être généré dans un tableau séparé avec un IDENTITY
comme substitut) si la valeur d'origine est très importante.
- changer le processus d'archivage pour utiliser une «suppression logicielle» où quelque chose est marqué comme archivé initialement et l'archivage n'est rendu permanent qu'à une date ultérieure. Quel que soit le processus qui tente de les reculer, il suffit d'effectuer une mise à jour directe et de corriger l'indicateur de suppression logicielle.