J'utilise une fonction T-SQL dans COALESCE
laquelle le premier argument ne sera pas nul environ 95% de son exécution. Si le premier argument est NULL
, le second argument est un processus assez long:
SELECT COALESCE(c.FirstName
,(SELECT TOP 1 b.FirstName
FROM TableA a
JOIN TableB b ON .....)
)
Si, par exemple, c.FirstName = 'John'
SQL Server exécutait-il toujours la sous-requête?
Je sais qu'avec la IIF()
fonction VB.NET , si le deuxième argument est True, le code lit toujours le troisième argument (même s'il ne sera pas utilisé).
CASE
on évalue toujours les circuits de gauche à droite et toujours courts. ).SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 1 END), 1);
- répéter plusieurs fois. Vous aurezNULL
parfois. Réessayez avecISNULL
- vous n'aurez jamaisNULL
...Qu'en est-il de celui-ci - comme cela m'a été rapporté par Itzik Ben-Gan, à qui Jaime Lafargue en a parlé ?
Résultat:
Il existe bien sûr des solutions de contournement triviales, mais le fait est que
CASE
cela ne garantit pas toujours une évaluation / un court-circuit de gauche à droite. J'ai signalé le bug ici et il a été fermé comme "par la conception." Paul White a ultérieurement classé cet élément Connect , qui a été fermé comme étant corrigé. Non pas parce que cela a été corrigé en soi, mais parce qu'ils ont mis à jour la documentation en ligne avec une description plus précise du scénario dans lequel les agrégats peuvent modifier l'ordre d'évaluation d'uneCASE
expression. J'ai récemment blogué plus à ce sujet ici .EDIT juste un addendum, bien que je convienne que ce sont des cas extrêmes, que la plupart du temps, vous pouvez vous fier à une évaluation de gauche à droite et à des courts-circuits, et que ce sont des bogues contrariant la documentation et susceptibles d'être corrigés ( ce n'est pas certain - consultez la conversation de suivi sur le blog de Bart Duncan pour savoir pourquoi), je ne suis pas d'accord quand des gens disent que quelque chose est toujours vrai même s'il y a un seul cas qui le réfute. Si Itzik et d’autres peuvent trouver des bogues solitaires comme celui-ci, il est au moins possible qu’il y ait d’autres bogues. Et comme nous ne connaissons pas le reste de la requête du PO, nous ne pouvons pas affirmer avec certitude qu'il s'en remettra à ce court-circuit mais finira par se faire mordre. Donc, pour moi, la réponse la plus sûre est:
Comme vous pouvez généralement vous fier
CASE
à l’évaluation gauche à droite et aux courts-circuits, comme indiqué dans la documentation, il n’est pas exact de dire que vous pouvez toujours le faire. Sur cette page, il existe deux cas de figure où ce n'est pas vrai et où aucun bogue n'a été corrigé dans aucune version publique de SQL Server.EDIT est un autre cas (je dois cesser de le faire) où une
CASE
expression ne s’évalue pas dans l’ordre que vous attendez, même si aucun agrégat n’est impliqué.la source
CASE
ça qui a été réglé tranquillementMon point de vue à ce sujet est que la documentation, il est assez clair que l' intention est que CASE devrait court-circuit. Comme Aaron le mentionne, il y a eu plusieurs cas (ha!) Où il a été démontré que cela n'était pas toujours vrai.
Jusqu'à présent, tous ces problèmes ont été reconnus comme des bogues et corrigés - mais pas nécessairement dans une version de SQL Server, vous pouvez acheter et mettre à jour aujourd'hui (le bogue de repliement constant n'a pas encore abouti à une mise à jour cumulative pour autant que je sache). Le plus récent bogue potentiel - signalé à l'origine par Itzik Ben-Gan - n'a pas encore été étudié (Aaron ou moi l'ajouterons à Connect prochainement).
En relation avec la question initiale, il existe d'autres problèmes avec CASE (et donc avec COALESCE) dans lesquels des fonctions ou des sous-requêtes ayant des effets secondaires sont utilisées. Considérer:
Le formulaire COALESCE renvoie souvent NULL, plus de détails à l' adresse https://connect.microsoft.com/SQLServer/feedback/details/546437/coalesce-subquery-1-may-return-null
Les problèmes démontrés avec les transformations de l'optimiseur et le suivi de l'expression commune signifient qu'il est impossible de garantir que CASE sera en court-circuit dans toutes les circonstances. Je peux concevoir des cas où il pourrait même ne pas être possible de prédire le comportement en inspectant la sortie du plan d'exposition publique, bien que je n'ai pas de repro pour cela aujourd'hui.
En résumé, je pense que vous pouvez être raisonnablement confiant que CASE court-circuitera en général (particulièrement si une personne assez qualifiée inspecte le plan d'exécution et que ce plan est "appliqué" avec un guide de planification ou des astuces), mais si vous en avez besoin une garantie absolue, vous devez écrire du SQL qui n'inclut pas du tout l'expression.
La situation n’est pas très satisfaisante, je suppose.
la source
J'ai rencontré un autre cas où
CASE
/COALESCE
ne court-circuite pas. La TVF suivante déclenchera une violation de clé PK si elle est transmise en1
tant que paramètre.Si appelé comme suit
Ou comme
Les deux donnent le résultat
montrant que la
SELECT
(ou au moins la population variable du tableau) est toujours exécutée et génère une erreur même si cette branche de la déclaration ne doit jamais être atteinte. Le plan pour laCOALESCE
version est ci-dessous.Cette réécriture de la requête semble éviter le problème
Qui donne plan
la source
Un autre exemple
La requête
Ne montre aucune lecture contre
T2
du tout.La recherche de
T2
est sous un prédicat passant et l'opérateur n'est jamais exécuté. MaisEst -ce que montre que
T2
est lu. Même si aucune valeur deT2
n'est jamais réellement nécessaire.Bien sûr, cela n’est pas vraiment surprenant, mais j’ai pensé qu’il valait la peine d’ajouter au référentiel contre-exemples, ne serait-ce que parce que cela pose la question de savoir ce que court-circuit signifie même dans un langage déclaratif basé sur un ensemble.
la source
Je voulais juste mentionner une stratégie que vous n'avez peut-être pas envisagée. Ce n'est peut-être pas un match, mais il est parfois utile. Voyez si cette modification vous donne de meilleures performances:
Une autre façon de le faire pourrait être ceci (fondamentalement équivalent, mais vous permet d'accéder à plus de colonnes de l'autre requête si nécessaire):
Fondamentalement, il s’agit d’une technique de jonction «dure» de tables, mais qui inclut la condition à laquelle toutes les lignes doivent être JOINÉES. D'après mon expérience, cela a parfois vraiment aidé les plans d'exécution.
la source
Non, ce ne serait pas. Il ne fonctionnera que quandc.FirstName
estNULL
.Cependant, vous devriez l'essayer vous-même. Expérience. Vous avez dit que votre sous-requête est longue. Référence. Tirez vos propres conclusions à ce sujet.La réponse de @Aaron sur la sous-requête en cours d'exécution est plus complète.
Cependant, je pense toujours que vous devriez retravailler votre requête et l'utiliser
LEFT JOIN
. La plupart du temps, les sous-requêtes peuvent être supprimées en retravaillant votre requête pour utiliserLEFT JOIN
s.Le problème lié à l'utilisation de sous-requêtes est que votre instruction globale s'exécutera plus lentement car la sous-requête est exécutée pour chaque ligne du jeu de résultats de la requête principale.
la source
La norme actuelle indique que toutes les clauses WHEN (ainsi que la clause ELSE) doivent être analysées pour déterminer le type de données de l'expression dans son ensemble. Je devrais vraiment sortir certaines de mes anciennes notes pour déterminer comment une erreur est gérée. Mais pour commencer, 1/0 utilise des entiers, je suppose donc que c’est une erreur. C'est une erreur avec le type de données entier. Lorsque vous n'avez que des valeurs nulles dans la liste de fusion, il est un peu plus difficile de déterminer le type de données, et c'est un autre problème.
la source