Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

In tsql un'istruzione Insert con un'istruzione Select è sicura in termini di concorrenza?

Come scrive Paolo:No, non è sicuro , per il quale vorrei aggiungere prove empiriche:crea una tabella Table_1 con un campo ID e un record con valore 0 . Quindi esegui il codice seguente contemporaneamente in due finestre di query di 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

Quindi esegui

SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1

Sul mio SQL Server 2008, un ID (662 ) è stato creato due volte. Pertanto, il livello di isolamento predefinito applicato alle singole istruzioni non sufficiente.

EDIT:Chiaramente, avvolgendo il INSERT con BEGIN TRANSACTION e COMMIT non lo risolverà, poiché il livello di isolamento predefinito per le transazioni è ancora READ COMMITTED , che non è sufficiente. Nota che impostando il livello di isolamento della transazione su REPEATABLE READ è anche non sufficiente. L'unico modo per rendere il codice sopra sicuro è aggiungere

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

in cima. Questo, tuttavia, ogni tanto provocava dei blocchi nei miei test.

EDIT:l'unica soluzione che ho trovato che è sicura e non produce deadlock (almeno nei miei test) è bloccare esplicitamente la tabella esclusivamente (il livello di isolamento della transazione predefinito è sufficiente qui). Attenzione però; questa soluzione potrebbe uccidere prestazioni:

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