Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

SE EXISTE, SELECIONE ELSE INSERT E DEPOIS SELECIONE


Você precisa fazer isso na transação para garantir que dois clientes simultâneos não insiram o mesmo fieldValue duas vezes:
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

você também pode usar bloqueio com verificação dupla para reduzir a sobrecarga de bloqueio
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

Quanto à necessidade de ISOLATION LEVEL SERIALIZABLE, quando você está dentro de uma transação serializável, o primeiro SELECT que atinge a tabela cria um range lock cobrindo o local onde o registro deveria estar, para que ninguém mais possa inserir o mesmo registro até que essa transação termine.

Sem ISOLATION LEVEL SERIALIZABLE, o nível de isolamento padrão (READ COMMITTED) não travaria a tabela em tempo de leitura, então entre SELECT e UPDATE, alguém ainda poderia inserir. As transações com nível de isolamento READ COMMITTED não causam o bloqueio de SELECT. As transações com REPEATABLE READS bloqueiam o registro (se encontrado), mas não a lacuna.