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

Violazione del vincolo UNIQUE KEY su INSERT WHERE COUNT(*) =0 su SQL Server 2005

Perché non funziona?

Credo che il comportamento predefinito di SQL Server sia quello di rilasciare i blocchi condivisi non appena non sono più necessari. La tua sottoquery comporterà un blocco condiviso (S) di breve durata sul tavolo, che verrà rilasciato non appena la sottoquery verrà completata.

A questo punto non c'è nulla che impedisca a una transazione simultanea di inserire la stessa riga che hai appena verificato non fosse presente.

Quale modifica devo apportare in modo che non vi sia alcuna possibilità di un'eccezione a causa della violazione del vincolo?

Aggiunta del HOLDLOCK suggerimento alla tua sottoquery indicherà a SQL Server di mantenere il blocco fino al completamento della transazione. (Nel tuo caso, questa è una transazione implicita.) Il HOLDLOCK hint è equivalente a SERIALIZABLE suggerimento, che a sua volta equivale al livello di isolamento della transazione serializzabile a cui fai riferimento nel tuo elenco di "altri approcci".

Il HOLDLOCK suggerimento da solo sarebbe sufficiente per mantenere il blocco S e impedire a una transazione simultanea di inserire la riga da cui ti stai proteggendo. Tuttavia, è probabile che il tuo errore di violazione della chiave univoca venga sostituito da deadlock, che si verificano con la stessa frequenza.

Se stai conservando solo un blocco S sul tavolo, considera una corsa tra due tentativi simultanei di inserire la stessa riga, procedendo in blocco -- entrambi riescono ad acquisire un blocco S sul tavolo, ma nessuno dei due riesce ad acquisire l'Esclusivo (X) blocco richiesto per eseguire l'inserimento.

Fortunatamente esiste un altro tipo di blocco per questo scenario esatto, chiamato blocco di aggiornamento (U). Il blocco U è identico a un blocco S con la seguente differenza:mentre più blocchi S possono essere mantenuti contemporaneamente sulla stessa risorsa, è possibile mantenere solo un blocco U alla volta. (Detto in altro modo, mentre i blocchi S sono compatibili tra loro (cioè possono coesistere senza conflitti), i blocchi U non sono compatibili tra loro, ma possono coesistere insieme ai blocchi S; e più avanti, i blocchi Exclusive (X) non lo sono compatibile con serrature S o U)

Puoi aggiornare il blocco S implicito nella tua sottoquery a un blocco U usando il UPDLOCK suggerimento.

Due tentativi simultanei di inserire la stessa riga nella tabella verranno ora serializzati all'istruzione select iniziale, poiché questa acquisisce (e mantiene) un blocco U, che non è compatibile con un altro blocco U dal tentativo di inserimento simultaneo.

Valori NULL

Un problema separato può sorgere dal fatto che FieldC consente valori NULL.

Se ANSI_NULLS è attivo (predefinito), quindi il controllo di uguaglianza FieldC=NULL restituirebbe false, anche nel caso in cui FieldC sia NULL (devi usare IS NULL operatore per verificare la presenza di null quando ANSI_NULLS è acceso). Poiché FieldC è annullabile, il controllo dei duplicati non funzionerà quando si inserisce un valore NULL.

Per gestire correttamente i null dovrai modificare la tua sottoquery EXISTS per utilizzare IS NULL operatore anziché = quando viene inserito un valore di NULL. (Oppure puoi modificare la tabella per non consentire NULL in tutte le colonne interessate.)

Riferimenti alla documentazione in linea di SQL Server

  • Suggerimenti per il blocco
  • Blocca matrice di compatibilità
  • ANSI_NULLS