J'ai récemment appris l'existence de la nouvelle clause "EXCEPT" dans SQL Server (un peu tard, je sais ...) grâce à la lecture de code écrit par un collègue. Cela m'a vraiment étonné!
Mais alors j'ai quelques questions concernant son utilisation: quand est-il recommandé d'être employé? Existe-t-il une différence, en termes de performances, entre son utilisation et une requête corrélée utilisant "ET NON EXISTE ..."?
Après avoir lu l'article d'EXCEPT dans le BOL, j'ai pensé que ce n'était qu'un raccourci pour la deuxième option, mais j'ai été surpris quand j'ai réécrit quelques requêtes en l'utilisant (donc ils avaient la syntaxe "ET NON EXISTE" beaucoup plus familière pour moi), puis j'ai vérifié les plans d'exécution - surprise! La version EXCEPT avait un plan d'exécution plus court et exécutée plus rapidement également. En est-il toujours ainsi?
J'aimerais donc savoir: quelles sont les directives pour utiliser cet outil puissant?
EXCEPT
traite les valeurs de NULL
comme des correspondances.
Cette requête:
WITH q (value) AS
(
SELECT NULL
UNION ALL
SELECT 1
),
p (value) AS
(
SELECT NULL
UNION ALL
SELECT 2
)
SELECT *
FROM q
WHERE value NOT IN
(
SELECT value
FROM p
)
renverra un ensemble de lignes vide.
Cette requête:
WITH q (value) AS
(
SELECT NULL
UNION ALL
SELECT 1
),
p (value) AS
(
SELECT NULL
UNION ALL
SELECT 2
)
SELECT *
FROM q
WHERE NOT EXISTS
(
SELECT NULL
FROM p
WHERE p.value = q.value
)
reviendra
NULL
1
, et celui-là:
WITH q (value) AS
(
SELECT NULL
UNION ALL
SELECT 1
),
p (value) AS
(
SELECT NULL
UNION ALL
SELECT 2
)
SELECT *
FROM q
EXCEPT
SELECT *
FROM p
retournera:
1
La référence récursive est également autorisée dans la clause EXCEPT
dans une clause récursive CTE
, bien qu'elle se comporte de manière étrange: elle renvoie tout sauf le dernière ligne d'un ensemble précédent, pas tout sauf l'ensemble précédent:
WITH q (value) AS
(
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
),
rec (value) AS
(
SELECT value
FROM q
UNION ALL
SELECT *
FROM (
SELECT value
FROM q
EXCEPT
SELECT value
FROM rec
) q2
)
SELECT TOP 10 *
FROM rec
---
1
2
3
-- original set
1
2
-- everything except the last row of the previous set, that is 3
1
3
-- everything except the last row of the previous set, that is 2
1
2
-- everything except the last row of the previous set, that is 3, etc.
1
SQL Server
les développeurs doivent juste avoir oublié de l'interdire.
J'ai fait beaucoup d'analyse de sauf, n'existe pas, pas dans et jointure externe gauche. Généralement, la jointure externe gauche est la plus rapide pour trouver les lignes manquantes, en particulier la jointure sur une clé primaire. Not In peut être très rapide si vous savez que ce sera une petite liste renvoyée dans la sélection.
J'utilise SAUF beaucoup pour comparer ce qui est retourné lors de la réécriture de code. Exécutez les anciens résultats d'enregistrement du code. Exécutez de nouveaux résultats d'enregistrement de code, puis utilisez except pour capturer toutes les différences. C'est un moyen très rapide et facile de trouver des différences, en particulier lorsque vous devez obtenir toutes les différences, y compris null. Très bon pour un codage facile à la volée.
Mais chaque situation est différente. Je dis à tous les développeurs que j'ai jamais encadrés. Essayez-le. Faites des synchronisations de différentes manières. Essayez-le, chronométrez-le, faites-le.
EXCEPT compare toutes les colonnes (appariées) de deux sélections complètes. NOT EXISTS compare deux tables ou plus selon les conditions spécifiées dans la clause WHERE dans la sous-requête suivant le mot clé NOT EXISTS.
EXCEPT peut être réécrit en utilisant NOT EXISTS. (SAUF TOUT peut être réécrit en utilisant ROW_NUMBER et NOT EXISTS.)
J'ai obtenu ça de ici
Si votre requête est affinée, il n'y a pas de différence de performances n/b en utilisant la clause EXCEPT et NOT EXIST/NOT IN .. la première fois que j'ai exécuté EXCEPT après y avoir changé ma requête corrélée .. J'ai été surpris car il est revenu avec le résultat juste en 7 secondes tandis que la requête corrélée revenait en 22 secondes .. alors j'ai utilisé une clause distincte dans ma requête corrélée et ré-exécutée .. elle est également revenue en 7 secondes .. donc EXCEPT est bon quand vous ne savez pas ou ne savez pas avoir le temps d'affiner votre requête, sinon les deux sont les mêmes en termes de performances.
Il n'y a pas de comptabilité pour les plans d'exécution de SQL Server. Lorsque j'ai des problèmes de performances, j'ai toujours trouvé que c'était tout à fait arbitraire (du point de vue d'un utilisateur, je suis sûr que les rédacteurs d'algorithmes comprendraient pourquoi) lorsqu'une syntaxe a fait un meilleur plan d'exécution plutôt qu'une autre.
Dans ce cas, quelque chose sur la comparaison des paramètres de requête permet à SQL de trouver un raccourci qu'il ne pouvait pas à partir d'une instruction de sélection directe. Je suis sûr que c'est une lacune dans l'algorithme. En d'autres termes, vous pouvez interpoler logiquement la même chose, mais l'algorithme ne fait pas cette traduction sur une requête existante. Parfois, c'est parce qu'un algorithme qui pourrait le comprendre de manière fiable prendrait plus de temps à exécuter que la requête elle-même, ou du moins le concepteur de l'algorithme le pensait.