Son collègue MVP Jamie Thomson a récemment souligné qu'il existe un bogue de "résultats erronés" dans SQL Server qui peut se manifester lorsque les conditions suivantes sont remplies :
- Vous avez une vue indexée qui joint au moins deux tables ;
- ces tables sont contraintes dans les deux sens par une clé étrangère à une seule colonne ;
- vous effectuez des mises à jour de la ou des tables de base à l'aide de
MERGE
qui inclut à la foisUPDATE
et (DELETE
ouINSERT
) Actions; et, - vous émettez par la suite des requêtes qui référencent l'index sur la vue (intentionnellement ou non).
Malheureusement, l'article de la base de connaissances décrivant le problème (KB #2756471) est assez léger sur les détails. Ils ne vous disent pas comment reproduire le problème, ni même ce que vous devriez rechercher spécifiquement pour voir si cela vous affecte; et ils ne mentionnent même pas MERGE
(qui est en fait le cœur du problème, pas NOEXPAND
, et non une simple mise à jour). Il y a quelques détails supplémentaires dans l'élément Connect qui ont provoqué le correctif; j'espère que l'article de la base de connaissances sera mis à jour avec plus de détails d'ici peu.
En attendant, le résultat que vous pouvez voir est des données incorrectes - ou mieux, des données obsolètes :La requête peut vous montrer l'ancienne version de la ou des lignes mises à jour ! J'ai passé quelques minutes à essayer de reproduire ce scénario dans AdventureWorks, et j'ai lamentablement échoué. Heureusement, Paul White (blog | @SQL_Kiwi) a écrit un article exceptionnel décrivant le scénario et montrant une reproduction complète du problème.
Je ne pense pas pouvoir souligner à quel point c'est sérieux.
Des millions de clients utilisent sûrement des vues indexées, beaucoup d'entre eux ont migré leur code DML pour utiliser MERGE
, et un grand nombre d'entre eux sont sur Enterprise Edition (ou ne le sont pas mais utilisent le NOEXPAND
indice ou font directement référence à l'index). Paul n'a pas tardé à souligner que NOEXPAND
n'est pas obligé de reproduire le problème dans Enterprise Edition, et a également découvert de nombreux autres détails nécessaires pour reproduire le bogue.
Ce message n'est pas destiné à voler le tonnerre des messages de Jamie ou de Paul ; juste une tentative de réitérer la préoccupation et de sensibiliser à cette question. Si vous avez l'habitude d'ignorer les mises à jour cumulatives, en choisissant d'attendre les Service Packs, et qu'il y a une chance que ce problème vous affecte en ce moment, vous vous devez, sans parler de vos parties prenantes et de vos clients, de prendre ce problème au sérieux.
Alors, que devez-vous faire ?
Eh bien, ce que vous faites ensuite dépend de la version et de l'édition de SQL Server que vous utilisez, et si le bogue vous affecte (ou pourrait) réellement vous affecter.
- Vous devez mettre à jour la dernière mise à jour cumulative pour votre succursale :
Branche Fixé en CU Construire Compilation minimale requise
pour appliquer la mise à jourArticle KB
(Télécharger)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 2012 Service Pack 1 CU #2 11.00.3339 11.00.3000 KB #2790947 Tableau 1 :Builds contenant le correctif
- Si vous n'appliquez pas le correctif, vous devez tester toutes les références à vos vues pour vérifier qu'elles renvoient des résultats corrects dans tous les cas, y compris après avoir mis à jour les tables de base à l'aide de
MERGE
. Si ce n'est pas le cas (ou si vous pensez qu'ils pourraient être affectés ultérieurement), vous devez reconstruire l'index clusterisé sur toutes les vues affectées (ou réparer la ou les vues indexées à l'aide deDBCC CHECKTABLE
, comme Paul l'a décrit dans son message), et arrêtez d'utiliserMERGE
contre ces tables jusqu'à ce que vous ayez appliqué le correctif. Si vous continuez à utiliserMERGE
contre les tables de base, préparez-vous à continuer à réparer les vues afin d'éviter le problème.
- Une solution plus rapide consisterait à empêcher l'utilisation de la vue indexée endommagée, en utilisant l'une des méthodes suivantes :
- appliquer l'indicateur de requête
OPTION (EXPAND VIEWS)
à toutes les requêtes pertinentes ; - supprimer toute référence explicite à l'index sur la vue ;
- dans les éditions Standard ou autres où les vues indexées ne correspondent pas automatiquement, supprimez toutes les instances de
NOEXPAND
.
Mais cela, bien sûr, irait à l'encontre de l'objectif de la vue indexée - autant supprimer l'index. Cela dit, il est généralement préférable d'obtenir les bons résultats lentement plutôt que d'obtenir rapidement les mauvais résultats. donc peut-être que ça va.
- appliquer l'indicateur de requête
SQL Server 2008 SP3
SQL Server 2008 R2 SP1/SP2
SQL Server 2012 RTM/SP1
Vos options si vous êtes sur l'une de ces versions :
SQL Server 2008 RTM/SP1/SP2
SQL Server 2008 R2 RTM
Malheureusement, vous êtes sur une version qui n'est plus prise en charge par le grand public et il est peu probable que ce problème soit résolu pour vous (sauf si vous bénéficiez d'une prise en charge étendue et que vous faites beaucoup de bruit). Vos options sont donc limitées ici - soit vous déplacez vers une branche prise en charge selon le tableau ci-dessus, et appliquez la mise à jour cumulative, soit choisissez l'une des autres options mentionnées précédemment.
SQL Server 2000
SQL Server 2005
Eh bien, la mauvaise nouvelle est que vous êtes également sur une version qui n'est plus prise en charge. La bonne nouvelle est que dans ce cas précis, cela n'a pas d'importance - vous ne pouvez pas utiliser MERGE
de toute façon, donc ce bogue ne peut pas vous affecter.
Autres problèmes de MERGE
Malheureusement, c'est loin d'être le premier bogue que nous avons vu avec MERGE
, et ce ne sera probablement pas le dernier. Voici une sélection rapide d'une douzaine MERGE
bugs toujours marqués comme actifs sur Connect :
- #773895 :MERGE signale à tort des violations de clé unique
- #766165 : MERGE évalue l'index filtré par ligne, et non après l'opération, ce qui entraîne une violation de l'index filtré
- #723696 :upsert MERGE de base provoquant des interblocages
- #713699 :Une vérification d'assertion système a échoué ("cxrowset.cpp":1528)
- #699055 :les plans de requête MERGE autorisent les violations des contraintes FK et CHECK
- #685800 :DELETE et MERGE paramétrés autorisent les violations de contrainte de clé étrangère
- #654746 :la fusion dans SQL2008 SP2 souffre toujours de "Tentative de définition de la valeur d'une colonne non-NULL-able sur NULL"
- #635778 :les parties NOT MATCHED et MATCHED d'une instruction SQL MERGE ne sont pas optimisées
- #633132 :FUSIONNER AVEC UNE SOURCE FILTRÉE ne fonctionne pas correctement
- #596086 :bogue de l'instruction MERGE lorsque INSERT/DELETE utilisait et filtrait l'index
- #583719 :L'instruction MERGE traite les colonnes calculées non nullables de manière incorrecte dans certains scénarios
- #539084 :MERGE Stmt :la condition de recherche sur une colonne non clé et un ORDER BY dans la table dérivée de la source rompent complètement la fusion
Maintenant, il se peut que certains de ces bogues aient été corrigés, mais leur statut est erroné car la boucle de retour à Connect n'a pas été fermée. Même si c'est le cas, cela ne peut pas être vrai pour tous (et potentiellement pour d'autres que je n'ai pas découverts).
De plus, il a été démontré par Dan Guzman que MERGE
n'est pas à l'abri des conditions de concurrence et d'autres problèmes de concurrence. La solution consiste à utiliser HOLDLOCK
(ou un niveau d'isolement supérieur); cependant, c'est une idée fausse commune que MERGE
est complètement atomique et n'est pas du tout sujet à ce problème. Par conséquent, je vais me demander à haute voix :combien de MERGE
les déclarations incluent HOLDLOCK
(ou sont exécutés sous SERIALIZABLE
) ? Combien d'entre eux ont été minutieusement testés pour les problèmes liés à la simultanéité ?
Conclusion
Personnellement, je pense que la syntaxe est excellente (bien que décourageante à apprendre), mais chaque fois qu'un problème survient, cela érode ma confiance dans l'aspect pratique du remplacement du DML existant par la nouvelle construction.
Dans cet esprit, ne pas être Chicken Little, mais je ne me sentirais pas à l'aise de recommander à quiconque d'utiliser MERGE
à moins qu'ils ne mettent en œuvre des tests extrêmement complets. Certains de ces problèmes sont également présents avec la norme UPSERT
méthodologies, mais les problèmes y sont plus évidents. MERGE
, simplement par sa nature à énoncé unique, vous donne envie de croire en la magie. Peut-être qu'un jour il tiendra ses promesses, mais pour l'instant je sais qu'il ne pourra pas scier une personne en deux sans une aide sérieuse.