Трябва да направите това в транзакция, за да сте сигурни, че двама едновременни клиенти няма да вмъкнат една и съща fieldValue два пъти:
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
можете също да използвате Заключване с двойна проверка за намаляване на разходите за заключване
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
Що се отнася до това защо е необходим SERIALIZABLE на НИВО НА ИЗОЛИРАНЕ, когато сте в транзакция, която може да бъде сериализирана, първият SELECT, който удари таблицата, създава заключване на диапазон, покриващо мястото, където трябва да бъде записът, така че никой друг не може да вмъкне същия запис, докато тази транзакция приключи.
Без ISOLATION LEVEL SERIALIZABLE нивото на изолация по подразбиране (READ COMMITTED) няма да заключи таблицата по време на четене, така че между SELECT и UPDATE някой все още ще може да вмъкне. Транзакциите с ниво на изолация READ COMMITTED не причиняват заключване на SELECT. Транзакциите с REPEATABLE READS заключват записа (ако бъде намерен), но не и пропуска.