Supponiamo che tu abbia il seguente codice (ignora che è terribile):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
A mio avviso, questo NON gestisce correttamente la concorrenza. Solo perché hai una transazione non significa che qualcun altro non leggerà lo stesso valore che hai fatto prima di arrivare alla tua dichiarazione di aggiornamento.
Ora, lasciando il codice così com'è (mi rendo conto che questo è meglio gestito come una singola istruzione o anche meglio usando una colonna di autoincremento / identità) quali sono i modi sicuri per farlo gestire correttamente la concorrenza e prevenire le condizioni di competizione che consentono a due clienti di ottenere lo stesso valore id?
Sono abbastanza sicuro che l'aggiunta di a WITH (UPDLOCK, HOLDLOCK)
a SELECT farà il trucco. Il livello di isolamento della transazione SERIALIZZABILE sembra funzionare anche poiché nega a chiunque altro di leggere ciò che hai fatto fino alla fine del tran ( AGGIORNAMENTO : questo è falso. Vedi la risposta di Martin). È vero? Funzioneranno entrambi allo stesso modo? L'uno è preferito all'altro?
Immagina di fare qualcosa di più legittimo di un aggiornamento ID: alcuni calcoli basati su una lettura che devi aggiornare. Potrebbero esserci molte tabelle coinvolte, alcune delle quali scriverai e altre no. Qual è la migliore pratica qui?
Dopo aver scritto questa domanda, penso che i suggerimenti per il blocco siano migliori perché in questo modo stai solo bloccando le tabelle di cui hai bisogno, ma apprezzerei l'input di chiunque.
PS E no, non conosco la risposta migliore e voglio davvero capire meglio! :)
update
che potrebbero essere basati su dati obsoleti? In quest'ultimo caso, puoi utilizzare larowversion
colonna per verificare se la riga da aggiornare non è stata modificata da quando è stata letta.