Som Paul skriver:Nej, det är inte säkert , som jag skulle vilja lägga till empiriska bevis för:Skapa en tabell Table_1
med ett fält ID
och en post med värdet 0
. Kör sedan följande kod samtidigt i två Management Studio-frågefönster :
declare @counter int
set @counter = 0
while @counter < 1000
begin
set @counter = @counter + 1
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
end
Kör sedan
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
På min SQL Server 2008, ett ID (662
) skapades två gånger. Således är standardisoleringsnivån som tillämpas på enstaka satser inte tillräckligt.
EDIT:Tydligen, linda in INSERT
med BEGIN TRANSACTION
och COMMIT
kommer inte att fixa det, eftersom standardisoleringsnivån för transaktioner fortfarande är READ COMMITTED
, vilket inte är tillräckligt. Observera att inställning av transaktionsisoleringsnivån till REPEATABLE READ
är också inte tillräckligt. Det enda sättet att göra ovanstående kod säker är att lägga till
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
på toppen. Detta orsakade dock dödlägen då och då i mina tester.
EDIT:Den enda lösningen jag hittade som är säker och inte producerar dödlägen (åtminstone i mina tester) är att explicit låsa tabellen exklusivt (standard transaktionsisoleringsnivå är tillräcklig här). Akta dig dock; den här lösningen kan döda prestanda:
...loop stuff...
BEGIN TRANSACTION
SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
COMMIT
...loop end...