Il collega MVP Jamie Thomson ha recentemente sottolineato che esiste un bug di "risultati errati" in SQL Server che può manifestarsi quando sono vere le seguenti condizioni:
- Hai una vista indicizzata che unisce almeno due tabelle;
- quelle tabelle sono vincolate in entrambe le direzioni da una chiave esterna a colonna singola;
- esegui gli aggiornamenti alle tabelle di base utilizzando
MERGE
che include siaUPDATE
e (DELETE
oINSERT
) Azioni; e, - successivamente esegui query che fanno riferimento all'indice nella vista (intenzionalmente o meno).
Sfortunatamente, l'articolo della Knowledge Base che descrive il problema (KB #2756471) è piuttosto chiaro sui dettagli. Non ti dicono come riprodurre il problema, e nemmeno cosa, in particolare, dovresti cercare per vedere se questo ti riguarda; e non fanno nemmeno menzione di MERGE
(che in realtà è il cuore del problema, non NOEXPAND
, e non un semplice aggiornamento). Ci sono alcuni dettagli aggiuntivi nell'elemento Connect che ha determinato la correzione; si spera che l'articolo della KB venga aggiornato con maggiori dettagli in breve tempo.
Nel frattempo, il risultato che potresti vedere sono dati errati o, meglio, dati obsoleti :La query potrebbe mostrarti la vecchia versione delle righe aggiornate! Ho trascorso alcuni minuti cercando di riprodurre questo scenario in AdventureWorks e ho fallito miseramente. Per fortuna, Paul White (blog | @SQL_Kiwi) ha scritto un post eccezionale descrivendo lo scenario e mostrando una riproduzione completa del problema.
Non credo di poter sottolineare quanto sia grave.
Sicuramente milioni di clienti utilizzano viste indicizzate, molti di questi hanno migrato il proprio codice DML per utilizzare MERGE
, e molti di questi sono su Enterprise Edition (o non lo sono, ma stanno usando il NOEXPAND
suggerimento o fanno riferimento direttamente all'indice). Paul si è affrettato a sottolineare che NOEXPAND
non è necessario riprodurre il problema in Enterprise Edition e ha anche scoperto molti altri dettagli necessari per riprodurre il bug.
Questo post non ha lo scopo di rubare alcun tuono dai post di Jamie o Paul; solo un tentativo di ribadire la preoccupazione e di sensibilizzare su questo tema. Se hai l'abitudine di ignorare gli aggiornamenti cumulativi, optando per attendere i Service Pack, e c'è qualche possibilità che questo problema possa interessarti in questo momento, devi farlo a te stesso, per non parlare dei tuoi stakeholder e clienti, di prendere seriamente questo problema.
Quindi cosa dovresti fare?
Bene, quello che fai dopo dipende dalla versione e dall'edizione di SQL Server in esecuzione e dal fatto che il bug ti riguardi (o potrebbe).
- Dovresti aggiornare all'ultimo aggiornamento cumulativo per la tua filiale:
Filiale Risolto in CU Costruisci Richiesta build minima
per applicare l'aggiornamentoArticolo KB
(Download)2008 Service Pack 3 CU #8 10.00.5828 10.00.5500 KB #2771833 2008 R2 Service Pack 1 CU #10 10.50.2868 10.50.2500 KB #2783135 2008 R2 Service Pack 2 CU #4 10.50.4270 10.00.4000 KB #2777358 RTM 2012 CU #5 11.00.2395 11.00.2100 KB #2777772 Service Pack 1 2012 CU #2 11.00.3339 11.00.3000 KB #2790947 Tabella 1:build che contengono la correzione
- Se non applichi la correzione, devi testare tutti i riferimenti alle tue viste per convalidare che restituiscano risultati corretti in tutti i casi, anche dopo aver aggiornato le tabelle di base usando
MERGE
. In caso contrario (o sospetti che potrebbero essere interessati in seguito), dovresti ricostruire l'indice cluster su tutte le viste interessate (o riparare le viste indicizzate utilizzandoDBCC CHECKTABLE
, come ha descritto Paul nel suo post) e smetti di usareMERGE
contro queste tabelle fino a quando non avrai applicato la correzione. Se continui a utilizzareMERGE
rispetto alle tabelle di base, prepararsi a continuare a riparare le viste per evitare il problema.
- Una soluzione più rapida sarebbe impedire l'utilizzo della vista indicizzata danneggiata, utilizzando uno dei seguenti metodi richiesti:
- applica il suggerimento per la query
OPTION (EXPAND VIEWS)
a tutte le domande pertinenti; - rimuove eventuali riferimenti espliciti all'indice nella vista;
- in Standard o in altre edizioni in cui le viste indicizzate non vengono abbinate automaticamente, rimuovi tutte le istanze di
NOEXPAND
.
Ma questo, ovviamente, vanificherebbe in gran parte lo scopo della vista indicizzata – potrebbe anche far cadere l'indice. Detto questo, di solito è meglio ottenere i risultati giusti lentamente, piuttosto che ottenere rapidamente i risultati sbagliati; quindi forse va bene.
- applica il suggerimento per la query
SQL Server 2008 SP3
SQL Server 2008 R2 SP1/SP2
SQL Server 2012 RTM/SP1
Le tue opzioni se sei su una di queste build:
SQL Server 2008 RTM/SP1/SP2
SQL Server 2008 R2 RTM
Sfortunatamente, sei su una build che non è più nel supporto mainstream ed è improbabile che questo problema venga risolto per te (a meno che tu non sia su supporto esteso e fai molto rumore). Quindi le tue opzioni sono limitate qui:passa a un ramo supportato secondo la tabella sopra e applica l'aggiornamento cumulativo, oppure scegli una delle altre opzioni menzionate in precedenza.
SQL Server 2000
SQL Server 2005
Bene, la cattiva notizia è che stai anche su una build che non è più supportata. La buona notizia è che in questo caso specifico non importa:non puoi usare MERGE
comunque, quindi questo bug non può interessarti.
Altri problemi di MERGE
Purtroppo, questo è lontano dal primo bug che abbiamo visto con MERGE
, e probabilmente non sarà l'ultimo. Ecco una rapida selezione di una dozzina MERGE
bug che sono ancora contrassegnati come attivi su Connect:
- #773895 :MERGE segnala erroneamente violazioni di chiavi univoche
- #766165 :MERGE valuta l'indice filtrato per riga, non l'operazione post, che causa la violazione dell'indice filtrato
- #723696 :Il MERGE di base viene ripristinato causando deadlock
- #713699 :Un controllo dell'asserzione di sistema non è riuscito ("cxrowset.cpp":1528)
- #699055:i piani di query MERGE consentono violazioni dei vincoli FK e CHECK
- #685800:DELETE e MERGE parametrizzati consentono violazioni dei vincoli di chiave esterna
- #654746:l'unione in SQL2008 SP2 soffre ancora di "Tentativo di impostare il valore di una colonna non NULL su NULL"
- #635778 :Le parti NOT MATCHED e MATCHED di un'istruzione SQL MERGE non sono ottimizzate
- #633132 :MERGE IN WITH FILTERED SOURCE non funziona correttamente
- #596086 :Bug dell'istruzione MERGE quando INSERT/DELETE utilizzato e indice filtrato
- #583719 :L'istruzione MERGE tratta le colonne calcolate non annullabili in modo errato in alcuni scenari
- #539084 :MERGE Stmt :la condizione di ricerca su una colonna non chiave e un ORDER BY nella tabella sorgente derivata interrompe MERGE completamente
Ora, potrebbe essere il caso che alcuni di questi bug siano stati effettivamente corretti, ma il loro stato è errato perché il loop di ritorno a Connect non è stato chiuso. Anche se è così, non può essere vero per tutti loro (e potenzialmente per altri che non ho scoperto).
Inoltre, Dan Guzman ha dimostrato che MERGE
non è immune da condizioni di razza e altri problemi di concorrenza. La soluzione alternativa consiste nell'usare HOLDLOCK
(o un livello di isolamento superiore); tuttavia, è un malinteso comune che MERGE
è completamente atomico e non è affatto soggetto a questo problema. Quindi mi chiederò ad alta voce:quanti MERGE
le dichiarazioni disponibili includono HOLDLOCK
(o sono in esecuzione sotto SERIALIZABLE
)? Quanti sono stati accuratamente testati per problemi relativi alla concorrenza?
Conclusione
Personalmente, penso che la sintassi sia ottima (anche se scoraggiante da imparare), ma ogni volta che si presenta un problema, erode la mia fiducia nella praticità di sostituire il DML esistente con il nuovo costrutto.
Con questo in mente, non per essere Chicken Little, ma non mi sentirei a mio agio nel consigliare a nessuno di usare MERGE
a meno che non implementino test estremamente completi. Alcuni di questi problemi sono presenti anche con UPSERT
standard metodologie, ma lì i problemi sono più evidenti. MERGE
, semplicemente per la sua natura di affermazione singola, ti fa venire voglia di credere nella magia. Forse un giorno reggerà, ma in questo momento so che non sarà in grado di vedere una persona a metà senza un aiuto serio.