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