Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

SI EXISTE, ALORS SÉLECTIONNER AUTREMENT INSÉRER PUIS SÉLECTIONNER

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.