sql >> Base de Datos >  >> RDS >> Sqlserver

SI EXISTE, ENTONCES SELECCIONE DE LO CONTRARIO INSERTAR Y LUEGO SELECCIONAR

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.