Както пише Павел:Не, не е безопасно , за което бих искал да добавя емпирични доказателства:Създайте таблица Table_1 с едно поле ID и един запис със стойност 0 . След това изпълнете следния код едновременно в два прозореца за заявка на 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
След това изпълнете
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
На моя SQL Server 2008, един идентификатор (662 ) е създаден два пъти. По този начин нивото на изолация по подразбиране, приложено към единични изрази, е не достатъчно.
РЕДАКТИРАНЕ:Ясно е, обвиване на INSERT с BEGIN TRANSACTION и COMMIT няма да го поправи, тъй като нивото на изолация по подразбиране за транзакциите все още е READ COMMITTED , което не е достатъчно. Имайте предвид, че задаване на нивото на изолация на транзакциите на REPEATABLE READ е също не е достатъчно. Единственият начин да направите горния код безопасен е да добавите
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
на върха. Това обаче от време на време предизвикваше безизходица в моите тестове.
РЕДАКТИРАНЕ:Единственото решение, което намерих, което е безопасно и не създава блокиране (поне в моите тестове) е изрично да заключи таблицата изключително (нивото на изолация на транзакциите по подразбиране е достатъчно тук). Внимавайте обаче; това решение може да убие изпълнение:
...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...