Requête MySQL "NOT IN"

181

Je voulais exécuter une requête simple pour afficher toutes les lignes Table1où une valeur de colonne principale n'est pas présente dans une colonne d'une autre table ( Table2).

J'ai essayé d'utiliser:

SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal

C'est plutôt une erreur de syntaxe. La recherche Google m'a conduit à des forums où les gens disaient que MySQL ne prend pas en charge NOT INet que quelque chose d'extrêmement complexe doit être utilisé. Est-ce vrai? Ou est-ce que je fais une terrible erreur?

Kshitij Saxena -KJ-
la source
1
Et si je veux des données similaires de trois tableaux. Je veux dire qu'une table1 a 2000 entrées, les deux autres tables 2 et 3 ont chacune 500 entrées, toutes ont un champ commun «nom». Comment pouvons-nous obtenir tous les détails du tableau 1 qui ne sont pas présents dans les tableaux 2 et 3 en fonction du «nom». Pouvons-nous utiliser NOT IN deux fois, si oui comment ..?

Réponses:

310

Pour utiliser IN, vous devez avoir un ensemble, utilisez plutôt cette syntaxe:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
Julien Lebosquain
la source
85
Attention quand table2.principalpeut l'être NULL. Dans ce cas NOT IN, retournera toujours FALSEcar NOT INest traité comme <> ALL, qui compare toutes les lignes de la sous-requête comme Table1.principal <> table2.principal, qui échoue lors de la comparaison avec NULL: Table1.principal <> NULLne donnera pas lieu à TRUE. Pour résoudre: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti
4
Merci pour le commentaire @Basti! J'ai passé beaucoup de temps à essayer de comprendre pourquoi la requête ne fonctionnait pas comme prévu.
gvas
3
N'oubliez pas d'éviter d'utiliser 'SELECT *' dans la liste 'NOT IN'. Vous devez choisir une colonne particulière. Sinon, vous obtiendrez cette erreur: stackoverflow.com/questions/14046838/…
Lorien Brune
165

L'option de sous-requête a déjà reçu une réponse, mais notez que dans de nombreux cas, un LEFT JOINpeut être un moyen plus rapide de le faire:

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

Si vous souhaitez vérifier plusieurs tables pour vous assurer qu'elles ne sont présentes dans aucune des tables (comme dans le commentaire de SRKR), vous pouvez utiliser ceci:

SELECT table1.*
FROM table1
LEFT JOIN table2 ON table2.name=table1.name
LEFT JOIN table3 ON table3.name=table1.name
WHERE table2.name IS NULL AND table3.name IS NULL
Lukáš Lalinský
la source
2
Dans mes propres tests, j'ai eu les mêmes performances pour les deux NOT IN& LEFT JOIN. +1 les deux
BufferStack
une fois la requête exécutée, vous devriez obtenir les mêmes résultats quoi qu'il arrive en raison de la mise en cache interne de la base de données
Toote
Pour moi, la performance était bien meilleure. J'ai parcouru différentes tables, alors qu'elles avaient des clés étrangères définies.
Keenora Fluffball
36

NOT IN vs NOT EXISTS vs LEFT JOIN / IS NULL dans MySQL

MySQL, ainsi que tous les autres systèmes à l'exception de SQL Server, est capable d'optimiser LEFT JOIN/IS NULL renvoyer FALSEdès que la valeur correspondante est trouvée, et c'est le seul système qui s'est soucié de documenter ce comportement. […] Puisque MySQL n'est pas capable d'utiliser HASHet de MERGEjoindre des algorithmes, le seul dont ANTI JOINil est capable est leNESTED LOOPS ANTI JOIN

[…]

Essentiellement, [ NOT IN] est exactement le même plan que LEFT JOIN/ IS NULLutilise, malgré le fait que ces plans sont exécutés par les différentes branches du code et qu'ils semblent différents dans les résultats de EXPLAIN. Les algorithmes sont en fait les mêmes en fait et les requêtes se terminent en même temps.

[…]

Il est difficile de dire la raison exacte de [baisse de performances lors de l'utilisation NOT EXISTS] , car cette baisse est linéaire et ne semble pas dépendre de la distribution des données, du nombre de valeurs dans les deux tables, etc., tant que les deux champs sont indexés. Puisqu'il y a trois morceaux de code dans MySQL qui font essentiellement un travail, il est possible que le code responsable deEXISTS fasse une sorte de vérification supplémentaire qui prend plus de temps.

[…]

MySQL peut optimiser les trois méthodes pour faire une sorte de NESTED LOOPS ANTI JOIN . […] Cependant, ces trois méthodes génèrent trois plans différents qui sont exécutés par trois morceaux de code différents. Le code qui exécute le EXISTSprédicat est environ 30% moins efficace […]

C'est pourquoi la meilleure façon de rechercher les valeurs manquantes dans MySQL consiste à utiliser un LEFT JOIN/ IS NULLouNOT IN plutôt que NOT EXISTS.

(emphase ajoutée)

moteur
la source
7

Malheureusement, cela semble être un problème avec l'utilisation de MySql de la clause "NOT IN", la capture d'écran ci-dessous montre l'option de sous-requête renvoyant des résultats erronés:

mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 1.1.8                        |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.5.21                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
7 rows in set (0.07 sec)

mysql> select count(*) from TABLE_A where TABLE_A.Pkey not in (select distinct TABLE_B.Fkey from TABLE_B );
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from TABLE_A left join TABLE_B on TABLE_A.Pkey = TABLE_B.Fkey where TABLE_B.Pkey is null;
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> select count(*) from TABLE_A where NOT EXISTS (select * FROM TABLE_B WHERE TABLE_B.Fkey = TABLE_A.Pkey );
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> 
Legna
la source