web-dev-qa-db-fra.com

Quand utiliser EXCEPT par opposition à NOT EXISTS dans Transact SQL?

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?

38
Joe Pineda

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.

37
Quassnoi

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.

8
Linda Warner

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

3
priyanka.sarkar

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.

2
Adil Aleem

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.

2
Yishai