Jak píše Paul:Ne, není to bezpečné , pro který bych rád přidal empirický důkaz:Vytvořte tabulku Table_1
s jedním polem ID
a jeden záznam s hodnotou 0
. Poté spusťte následující kód současně ve dvou oknech dotazu Management Studio :
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
Poté proveďte
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
Na mém serveru SQL Server 2008 je jedno ID (662
) byl vytvořen dvakrát. Výchozí úroveň izolace aplikovaná na jednotlivé příkazy je tedy ne dostačující.
EDIT:Jasně, zalomení INSERT
s BEGIN TRANSACTION
a COMMIT
neopraví to, protože výchozí úroveň izolace pro transakce je stále READ COMMITTED
, což není dostatečné. Pamatujte, že nastavení úrovně izolace transakcí na REPEATABLE READ
je také nedostatečný. Jediný způsob, jak učinit výše uvedený kód bezpečným je přidat
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
Nahoře. To však v mých testech tu a tam způsobovalo uváznutí.
EDIT:Jediné řešení, které jsem našel, je bezpečné a nevytváří uváznutí (alespoň v mých testech) je explicitně zamknout výhradně tabulku (výchozí úroveň izolace transakcí je zde dostatečná). Dejte si však pozor; toto řešení může zabíjet výkon:
...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...