Je voulais exécuter une requête simple pour afficher toutes les lignes de Table1
où 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
Cela provoque une erreur de syntaxe. La recherche Google m'a conduit à des forums où les gens disaient que MySQL ne supporte pas NOT IN
et que quelque chose d'extrêmement complexe doit être utilisé. Est-ce vrai? Ou est-ce que je commets une terrible erreur?
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)
L'option de sous-requête a déjà été répondue, mais notez que dans de nombreux cas, un LEFT JOIN
peut être un moyen plus rapide de procéder:
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 figurent dans aucune de ces 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
MySQL, comme tous les autres systèmes à l'exception de SQL Server, est capable d'optimiser
LEFT JOIN
_/_IS NULL
pour renvoyerFALSE
dès que la valeur correspondante est trouvée, et c'est le seul système qui se souciait de documenter ce comportement. […] Puisque MySQL n'est pas capable d'utiliser les algorithmes de jointureHASH
etMERGE
, le seul _ANTI JOIN
_ dont il est capable est le _NESTED LOOPS ANTI JOIN
_
[…]
Essentiellement, [
NOT IN
] correspond exactement au même plan que _LEFT JOIN
_/_IS NULL
_, malgré le fait ces plans sont exécutés par les différentes branches de code et ils ont une apparence différente dans les résultats deEXPLAIN
. Les algorithmes sont en fait les mêmes et les requêtes se terminent en même temps.
[…]
Il est difficile de dire la raison exacte de [chute de performance lors de l'utilisation de
NOT EXISTS
] , car cette chute est linéaire et ne semble pas dépendre de distribution des données, nombre de valeurs dans les deux tables, etc., tant que les deux champs sont indexés. Étant donné que MySQL comporte trois éléments de code qui font essentiellement un travail, il est possible que le code responsable deEXISTS
effectue une sorte de vérification supplémentaire qui prend du temps supplémentaire.
[…]
MySQL peut optimiser les trois méthodes pour créer 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 prédicatEXISTS
est environ 30% moins efficace […]C’est la raison pour laquelle le meilleur moyen de rechercher les valeurs manquantes dans MySQL consiste à utiliser un _
LEFT JOIN
_/_IS NULL
_ ou _NOT IN
_ plutôt que _NOT EXISTS
_.
(emphases ajoutées)
Malheureusement, l'utilisation de la clause "NOT IN" dans MySql semble poser problème, 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>
Attention NOT IN
n'est pas un alias pour <> ANY
, mais pour <> ALL
!
http://dev.mysql.com/doc/refman/5.0/en/any-in-some-subqueries.html
SELECT c FROM t1 LEFT JOIN t2 USING (c) WHERE t2.c IS NULL
ne peut pas être remplacé par
SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)
Tu dois utiliser
SELECT c FROM t1 WHERE c <> ANY (SELECT c FROM t2)