Vous devez le faire dans la transaction pour vous assurer que deux clients simultanés n'inséreront pas deux fois la même valeur de champ :
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
DECLARE @id AS INT
SELECT @id = tableId FROM table WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
SELECT @id
COMMIT TRANSACTION
vous pouvez également utiliser Verrouillage à double contrôle pour réduire les frais généraux de verrouillage
DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @id = tableID FROM table WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
COMMIT TRANSACTION
END
SELECT @id
Quant à savoir pourquoi ISOLATION LEVEL SERIALIZABLE est nécessaire, lorsque vous êtes à l'intérieur d'une transaction sérialisable, le premier SELECT qui frappe la table crée un verrou de plage couvrant l'endroit où l'enregistrement devrait être, afin que personne d'autre ne puisse insérer le même enregistrement jusqu'à ce que cette transaction se termine.
Sans ISOLATION LEVEL SERIALIZABLE, le niveau d'isolement par défaut (READ COMMITTED) ne verrouillerait pas la table au moment de la lecture, donc entre SELECT et UPDATE, quelqu'un pourrait toujours insérer. Les transactions avec le niveau d'isolement READ COMMITTED n'entraînent pas le verrouillage de SELECT. Les transactions avec REPEATABLE READS verrouillent l'enregistrement (s'il est trouvé) mais pas l'écart.