Como Paul escreve:Não, não é seguro , para o qual gostaria de adicionar evidências empíricas:Crie uma tabela
Table_1
com um campo ID
e um registro com valor 0
. Em seguida, execute o seguinte código simultaneamente em duas janelas de consulta do 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
Em seguida, execute
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
No meu SQL Server 2008, um ID (
662
) foi criado duas vezes. Assim, o nível de isolamento padrão aplicado a instruções únicas não suficiente. EDIT:Claramente, envolvendo o
INSERT
com BEGIN TRANSACTION
e COMMIT
não irá corrigi-lo, pois o nível de isolamento padrão para transações ainda é READ COMMITTED
, o que não é suficiente. Observe que definir o nível de isolamento da transação para REPEATABLE READ
é também não suficiente. A única maneira de tornar o código acima seguro é adicionar SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
no topo. Isso, no entanto, causou impasses de vez em quando em meus testes.
EDIT:A única solução que encontrei que é segura e não produz deadlocks (pelo menos em meus testes) é bloquear explicitamente a tabela exclusivamente (o nível de isolamento de transação padrão é suficiente aqui). Cuidado, porém; esta solução pode matar atuação:
...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...