Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

No tsql, um Insert com uma instrução Select é seguro em termos de simultaneidade?


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...