Som Paulus skriver:Nej, det er ikke sikkert , som jeg gerne vil tilføje empirisk bevis for:Opret en tabel Table_1
med ét felt ID
og én post med værdien 0
. Udfør derefter følgende kode samtidigt i to Management Studio-forespørgselsvinduer :
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
Udfør derefter
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
På min SQL Server 2008, ét ID (662
) blev oprettet to gange. Således er standardisolationsniveauet anvendt på enkelte sætninger ikke tilstrækkeligt.
EDIT:Det er klart, indpakning af INSERT
med BEGIN TRANSACTION
og COMMIT
løser det ikke, da standardisolationsniveauet for transaktioner stadig er READ COMMITTED
, hvilket ikke er tilstrækkeligt. Bemærk, at indstilling af transaktionsisolationsniveauet til REPEATABLE READ
er også ikke tilstrækkeligt. Den eneste måde at gøre ovenstående kode sikker på er at tilføje
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
på toppen. Dette forårsagede dog deadlocks nu og da i mine tests.
EDIT:Den eneste løsning, jeg fandt, er sikker og ikke producerer deadlocks (i hvert fald i mine tests) er eksplicit at låse tabellen udelukkende (standard transaktionsisolationsniveau er tilstrækkeligt her). Pas dog på; denne løsning kan dræbe ydeevne:
...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...