Nous avons rencontré un problème intéressant avec SQL Server. Prenons l'exemple de repro suivant:
CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');
SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
AND s_guid <> NEWID();
DROP TABLE #test;
Veuillez oublier un instant que la s_guid <> NEWID()
condition semble entièrement inutile - ce n'est qu'un exemple de repro minimal. Étant donné que la probabilité de NEWID()
correspondre à une valeur constante donnée est extrêmement faible, elle devrait être évaluée à VRAI à chaque fois.
Mais ce n'est pas le cas. L'exécution de cette requête renvoie généralement 1 ligne, mais parfois (assez fréquemment, plus d'une fois sur 10) renvoie 0 ligne. Je l'ai reproduit avec SQL Server 2008 sur mon système, et vous pouvez le reproduire en ligne avec le violon lié ci-dessus (SQL Server 2014).
L'examen du plan d'exécution révèle que l'analyseur de requêtes divise apparemment la condition en s_guid < NEWID() OR s_guid > NEWID()
:
... ce qui explique complètement pourquoi il échoue parfois (si le premier ID généré est plus petit et le second plus grand que l'ID donné).
SQL Server est-il autorisé à évaluer en A <> B
tant que A < B OR A > B
, même si l'une des expressions n'est pas déterministe? Si oui, où est-il documenté? Ou avons-nous trouvé un bug?
Fait intéressant, AND NOT (s_guid = NEWID())
donne le même plan d'exécution (et le même résultat aléatoire).
Nous avons constaté ce problème lorsqu'un développeur souhaitait éventuellement exclure une ligne particulière et utilisait:
s_guid <> ISNULL(@someParameter, NEWID())
comme "raccourci" pour:
(@someParameter IS NULL OR s_guid <> @someParameter)
Je recherche de la documentation et / ou la confirmation d'un bug. Le code n'est pas tout à fait pertinent, donc des solutions de contournement ne sont pas nécessaires.
la source
Réponses:
C'est un point quelque peu controversé, et la réponse est un "oui" nuancé.
La meilleure discussion dont j'ai connaissance a été donnée en réponse au rapport de bogue Connect d'Itzik Ben-Gan, Bug avec NEWID et Expressions de table , qui a été fermé car il ne sera pas corrigé. Connect a depuis été retiré, donc le lien est là vers une archive Web. Malheureusement, beaucoup de matériel utile a été perdu (ou rendu plus difficile à trouver) par la disparition de Connect. Quoi qu'il en soit, les citations les plus utiles de Jim Hogg de Microsoft sont:
Un exemple du changement de comportement à cet égard au fil du temps est NULLIF fonctionne incorrectement avec des fonctions non déterministes telles que RAND () . Il existe également d'autres cas similaires utilisant par exemple
COALESCE
une sous-requête qui peuvent produire des résultats inattendus et qui sont également traités progressivement.Jim poursuit:
Ceci est une conséquence de la normalisation, qui se produit très tôt lors de la compilation des requêtes. Les deux expressions se compilent exactement sous la même forme normalisée, de sorte que le même plan d'exécution est produit.
la source
Ceci est documenté (en quelque sorte) ici:
Fonctions définies par l'utilisateur
Ce n'est pas le seul formulaire de requête dans lequel le plan de requête exécutera NEWID () plusieurs fois et modifiera le résultat. Ceci est déroutant, mais est en fait critique pour que NEWID () soit utile pour la génération de clés et le tri aléatoire.
Le plus déroutant est que toutes les fonctions non déterministes ne se comportent pas vraiment comme ça. Par exemple, RAND () et GETDATE () ne s'exécuteront qu'une seule fois par requête.
la source
=
,<
et>
peut être évalué efficacement par rapport à un BTree.Pour ce qu'il vaut, si vous regardez cet ancien document standard SQL 92 , les exigences concernant l'inégalité sont décrites dans la section "
8.2 <comparison predicate>
" comme suit:Remarque: J'ai inclus 7b et 7h pour être complet car ils parlent de
<>
comparaison - je ne pense pas que la comparaison des constructeurs de valeurs de ligne avec plusieurs valeurs soit implémentée dans T-SQL, à moins que je ne comprenne simplement massivement ce que cela dit - ce qui est tout à fait possibleCeci est un tas d'ordures déroutantes. Mais si vous voulez continuer à plonger dans des bennes à ordures ...
Je pense que 1.ii est l'élément qui s'applique dans ce scénario, car nous comparons les valeurs des "éléments de constructeur de valeur de ligne".
Fondamentalement, il dit que
X <> Y
c'est vrai si les valeurs représentées par X et Y ne sont pas égales. Puisqu'ilX < Y OR X > Y
s'agit d'une réécriture logiquement équivalente de ce prédicat, il est tout à fait cool pour l'optimiseur de l'utiliser.La norme n'impose aucune contrainte à cette définition liée à la déterminisme (ou autre chose, vous l'obtenez) des éléments du constructeur de la valeur de ligne de chaque côté de l'
<>
opérateur de comparaison. C'est la responsabilité du code utilisateur de gérer le fait qu'une expression de valeur d'un côté peut être non déterministe.la source