Quelle est la différence entre NOT EXISTS vs NOT IN vs LEFT JOIN WHERE IS NULL?


151

Il me semble que vous pouvez faire la même chose dans une requête SQL en utilisant soit NOT EXISTS, NOT IN ou LEFT JOIN WHERE IS NULL. Par exemple:

SELECT a FROM table1 WHERE a NOT IN (SELECT a FROM table2)

SELECT a FROM table1 WHERE NOT EXISTS (SELECT * FROM table2 WHERE table1.a = table2.a)

SELECT a FROM table1 LEFT JOIN table2 ON table1.a = table2.a WHERE table1.a IS NULL

Je ne suis pas sûr que toute la syntaxe soit correcte, mais ce sont les techniques générales que j'ai vues. Pourquoi choisirais-je d'utiliser l'un plutôt que l'autre? Les performances diffèrent-elles ...? Lequel de ceux-ci est le plus rapide / le plus efficace? (Si cela dépend de la mise en œuvre, quand utiliserais-je chacun d'eux?)


6
De nombreux moteurs SQL courants vous permettent de voir un plan d'exécution. Vous pouvez souvent détecter des différences significatives d'efficacité pour des requêtes logiquement équivalentes de cette manière. Le succès de toute méthode dépend de facteurs tels que la taille de la table, les index présents et autres.
Chris Farmer

2
@wich: aucune base de données ne se soucie de ce que vous renvoyez exactement dans la EXISTSclause. Vous pouvez revenir *, NULLou autre: tout cela sera optimisé.
Quassnoi le

2
@wich - pourquoi? Les deux ici: techonthenet.com/sql/exists.php et ici: msdn.microsoft.com/en-us/library/ms188336.aspx semblent utiliser * ...
froadie

8
@wich: il ne s'agit pas "d'exprimer son intérêt". Il s'agit de l'analyseur de requêtes qui vous demande de mettre quelque chose entre SELECTet FROM. Et *c'est juste plus facile à taper. Oui, SQLcela ressemble à un langage naturel, mais il est analysé et exécuté par une machine, une machine programmée. Ce n'est pas qu'il va soudainement s'introduire dans votre armoire et crier "Arrêtez de demander les champs supplémentaires dans une EXISTSrequête parce que j'en ai marre de les analyser et de les jeter!". C'est vraiment OK avec un ordinateur.
Quassnoi

1
@Quassnoi si vous écriviez du code dans le seul but de l'interpréter par une machine, le code aurait l'air horrible, et malheureusement beaucoup de gens travaillent comme ça. Si toutefois vous écrivez du code dans une autre optique, écrivez du code pour exprimer ce que vous voulez que la machine fasse comme un communiqué à vos pairs, vous écrirez un code meilleur et plus maintenable. Soyez intelligent, écrivez du code pour les gens, pas pour l'ordinateur.
qui le

Réponses:


139

En un mot:

NOT INest un peu différent: il ne correspond jamais s'il n'y en a qu'un seul NULLdans la liste.

  • En MySQL, NOT EXISTSest un peu moins efficace

  • Dans SQL Server, LEFT JOIN / IS NULLest moins efficace

  • Dans PostgreSQL, NOT INest moins efficace

  • En Oracle, les trois méthodes sont identiques.


1
Merci pour les liens! Et merci pour cet aperçu rapide ... Mon bureau bloque le lien pour une raison quelconque: P mais je vais le vérifier dès que j'arrive à un ordinateur ordinaire.
froadie

2
Un autre point est que si table1 .acontient NULLla EXISTSrequête ne renverra pas cette ligne mais la NOT INrequête sera si table2vide. NOT IN vs NOT EXISTS Nullable Columns: SQL Server
Martin Smith

@MartinSmith: NULL NOT IN ()évalue à vrai (pas NULL), tout commeNOT EXISTS (NULL = column)
Quassnoi

2
@Quassnoi - euh, bon point, je l'ai mal compris. Le NOT EXISTSretournera toujours la ligne mais NOT INne le fera que si la sous-requête ne renvoie aucune ligne.
Martin Smith

5

Si la base de données est bonne pour optimiser la requête, les deux premiers seront transformés en quelque chose de proche du troisième.

Pour des situations simples comme celles que vous interrogez, il devrait y avoir peu ou pas de différence, car elles seront toutes exécutées en tant que jointures. Dans les requêtes plus complexes, la base de données peut ne pas être en mesure de créer une jointure à partir des requêtes not inet not exists. Dans ce cas, les requêtes seront beaucoup plus lentes. D'un autre côté, une jointure peut également mal fonctionner s'il n'y a pas d'index pouvant être utilisé, donc ce n'est pas parce que vous utilisez une jointure que vous êtes en sécurité. Vous devrez examiner le plan d'exécution de la requête pour savoir s'il peut y avoir des problèmes de performances.


2

En supposant que vous évitiez les nulls, ce sont tous des moyens d'écrire une anti-jointure aide de SQL standard.

Une omission évidente est l'équivalent en utilisant EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Notez que dans Oracle, vous devez utiliser l' MINUSopérateur (sans doute un meilleur nom):

SELECT a FROM table1
MINUS
SELECT a FROM table2

En parlant de syntaxe propriétaire, il peut également y avoir des équivalents non standard qui méritent d'être étudiés en fonction du produit que vous utilisez, par exemple OUTER APPLYdans SQL Server (quelque chose comme):

SELECT t1.a
  FROM table1 t1
       OUTER APPLY 
       (
        SELECT t2.a
          FROM table2 t2
         WHERE t2.a = t1.a
       ) AS dt1
 WHERE dt1.a IS NULL;

0

Lorsqu'il est nécessaire d'insérer des données dans une table avec une clé primaire multi-champs, considérez qu'il sera beaucoup plus rapide (j'ai essayé dans Access mais je pense dans n'importe quelle base de données) de ne pas vérifier "qu'il n'existe pas d'enregistrements avec de telles valeurs dans la table", - plutôt simplement insérer dans la table, et les enregistrements excédentaires (par la clé) ne seront pas insérés deux fois.


0

La perspective des performances évite toujours d'utiliser des mots-clés inverses tels que NOT IN, NOT EXISTS, ... Parce que pour vérifier les éléments inverses, le SGBD doit parcourir tous les éléments disponibles et supprimer la sélection inverse.


1
Et que proposez-vous comme solution de contournement lorsque vous en avez réellement besoin NOT?
dnoeth le

Eh bien, quand il n'y a pas d'option de cause, nous devons utiliser les opérations NOT et c'est pourquoi elles existent. La meilleure pratique consiste à les éviter lorsque nous avons d'autres solutions alternatives.
Lahiru Cooray

@onedaywhen, si un optimiseur transforme une requête et qu'il renvoie le mauvais résultat, il s'agit d'un bug
David דודו Markovitz

@DuduMarkovitz: oui et si vous contactez l'équipe SQL Server et qu'ils reconnaissent le bogue mais refusent de le corriger car ils disent que cela peut ralentir les requêtes, alors c'est un bogue que vous devez gérer .
jour du

@onedaywhen - Ce n'était pas un scénario hypothétique, je suppose :-) Vous souvenez-vous par hasard des détails du bogue?
David דודו Markovitz
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.