Du måste göra detta i transaktionen för att säkerställa att två samtidiga klienter inte kommer att infoga samma fieldValue två gånger:
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
du kan också använda Dubbelkontrollerad låsning för att minska låsningsoverhead
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
Angående varför ISOLATION LEVEL SERIALIZABLE är nödvändigt, när du är inne i en serialiserbar transaktion, skapar den första SELECT som träffar tabellen ett intervalllås som täcker platsen där posten ska vara, så att ingen annan kan infoga samma post förrän denna transaktion avslutas.
Utan ISOLATION LEVEL SERIALIZABLE, skulle standardisoleringsnivån (READ COMMITTED) inte låsa tabellen vid läsningstidpunkten, så mellan SELECT och UPDATE skulle någon fortfarande kunna infoga. Transaktioner med READ COMMITTED isoleringsnivå orsakar inte att SELECT låses. Transaktioner med REPEATABLE READS låser posten (om den hittas) men inte gapet.