Debe hacer esto en la transacción para asegurarse de que dos clientes simultáneos no inserten el mismo valor de campo dos veces:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
DECLARE @id AS INT
SELECT @id = tableId FROM table WHERE [email protected]
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
SELECT @id
COMMIT TRANSACTION
también puede usar Bloqueo de verificación doble para reducir la sobrecarga de bloqueo
DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE [email protected]
IF @id IS NULL
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @id = tableID FROM table WHERE [email protected]
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
COMMIT TRANSACTION
END
SELECT @id
En cuanto a por qué es necesario ISOLATION LEVEL SERIALIZABLE, cuando está dentro de una transacción serializable, el primer SELECT que golpea la tabla crea un bloqueo de rango que cubre el lugar donde debería estar el registro, para que nadie más pueda insertar el mismo registro hasta que finalice esta transacción.
Sin ISOLATION LEVEL SERIALIZABLE, el nivel de aislamiento predeterminado (READ COMMITTED) no bloquearía la tabla en el momento de la lectura, por lo que entre SELECCIONAR y ACTUALIZAR, alguien aún podría insertar. Las transacciones con nivel de aislamiento de LECTURA COMPROMETIDA no provocan el bloqueo de SELECT. Las transacciones con LECTURAS REPETIBLES bloquean el registro (si se encuentra) pero no la brecha.