Musisz to zrobić w transakcji, aby zapewnić, że dwaj klienci jednocześnie nie wstawią tej samej wartości pola dwukrotnie:
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
możesz również użyć Podwójnie sprawdzane blokowanie aby zmniejszyć obciążenie związane z blokowaniem
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
Jeśli chodzi o to, dlaczego ISOLATION LEVEL SERIALIZABLE jest konieczne, kiedy znajdujesz się w transakcji, którą można serializować, pierwszy SELECT, który trafi do tabeli, tworzy blokadę zakresu obejmującą miejsce, w którym powinien znajdować się rekord, aby nikt inny nie mógł wstawić tego samego rekordu, dopóki transakcja się nie zakończy.
Bez ISOLATION LEVEL SERIALIZABLE domyślny poziom izolacji (READ COMMITTED) nie blokowałby tabeli w czasie odczytu, więc między SELECT a UPDATE ktoś nadal będzie mógł wstawić. Transakcje z poziomem izolacji READ COMMITTED nie powodują zablokowania SELECT. Transakcje z POWTARZALNYMI ODCZYTAMI blokują rekord (jeśli został znaleziony), ale nie lukę.