Consulta MySQL "NO EN"

181

Quería ejecutar una consulta simple para arrojar todas las filas de Table1donde un valor de columna principal no está presente en una columna en otra tabla ( Table2).

Intenté usar:

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

Esto, en cambio, arroja un error de sintaxis. La búsqueda en Google me llevó a foros donde la gente decía que MySQL no es compatible NOT INy que algo extremadamente complejo debe ser utilizado. ¿Es esto cierto? ¿O estoy cometiendo un error horrendo?

Kshitij Saxena -KJ-
fuente
1
Y qué pasa si quiero datos similares de tres tablas. Quiero decir que una tabla1 tiene 2000 entradas, las otras dos tablas 2 y 3 tienen cada una 500 entradas, todas tienen un campo común 'nombre'. ¿Cómo podemos obtener todos los detalles de la tabla 1 que no están presentes en las tablas 2 y 3 basados ​​en 'nombre'? ¿Podemos usar NOT IN dos veces, si es así, cómo ...?

Respuestas:

310

Para usar IN, debe tener un conjunto, use esta sintaxis en su lugar:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
Julien Lebosquain
fuente
85
Cuidado cuando table2.principalpuede ser NULL. En ese caso NOT IN, siempre se devolverá FALSEporque NOT INse trata como <> ALL, que compara todas las filas de la subconsulta como Table1.principal <> table2.principal, lo que falla cuando se compara con NULL: Table1.principal <> NULLno dará como resultado TRUE. Para solucionar: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti
44
Gracias por el comentario @Basti! Pasé mucho tiempo tratando de entender por qué la consulta no funcionaba como se esperaba.
gvas
3
No olvide evitar usar 'SELECT *' dentro de la lista 'NOT IN'. Debes elegir una columna en particular. De lo contrario, obtendrá este error: stackoverflow.com/questions/14046838/…
Lorien Brune
165

La opción de subconsulta ya ha sido respondida, pero tenga en cuenta que en muchos casos a LEFT JOINpuede ser una forma más rápida de hacer esto:

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

Si desea verificar varias tablas para asegurarse de que no esté presente en ninguna de las tablas (como en el comentario de SRKR), puede usar esto:

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ý
fuente
2
En mis propias pruebas, tuve el mismo rendimiento para ambos NOT INy LEFT JOIN. +1 ambos
BufferStack
una vez que el RAN consulta una vez que usted debe obtener los mismos resultados sin importar lo que debido a la caché de base de datos interna
Toote
Para mí, el rendimiento fue mucho mejor. Repasé diferentes tablas, mientras tenían claves externas configuradas.
Keenora Fluffball
36

NO ESTÁ EN CONTRA NO EXISTE en ÚNICO IZQUIERDO / ES NULO en MySQL

MySQL, así como todos los demás sistemas, excepto SQL Server, puede optimizar LEFT JOIN/IS NULL regresar FALSEtan pronto como se encuentre el valor coincidente, y es el único sistema que se ocupó de documentar este comportamiento. […] Dado que MySQL no es capaz de usar HASHy MERGEunir algoritmos, lo único ANTI JOINque puede hacer esNESTED LOOPS ANTI JOIN

[...]

Esencialmente, [ NOT IN] es exactamente el mismo plan que LEFT JOIN/ IS NULLutiliza, a pesar de que estos planes son ejecutados por las diferentes ramas del código y se ven diferentes en los resultados de EXPLAIN. De hecho, los algoritmos son los mismos y las consultas se completan al mismo tiempo.

[...]

Es difícil decir la razón exacta de [caída del rendimiento cuando se usa NOT EXISTS] , ya que esta caída es lineal y no parece depender de la distribución de datos, el número de valores en ambas tablas, etc., siempre que ambos campos estén indexados. Dado que hay tres partes de código en MySQL que esencialmente hacen un trabajo, es posible que el código responsable EXISTSrealice algún tipo de verificación adicional que requiera más tiempo.

[...]

MySQL puede optimizar los tres métodos para hacer una especie de NESTED LOOPS ANTI JOIN. […] Sin embargo, estos tres métodos generan tres planes diferentes que se ejecutan mediante tres partes diferentes de código. El código que ejecuta el EXISTSpredicado es aproximadamente un 30% menos eficiente […]

Es por eso que la mejor manera de buscar valores perdidos en MySQL es usar a LEFT JOIN/ IS NULLo en NOT INlugar de NOT EXISTS.

(énfasis agregado)

engin
fuente
7

Desafortunadamente, parece ser un problema con el uso de MySql de la cláusula "NOT IN", la captura de pantalla a continuación muestra la opción de subconsulta que devuelve resultados incorrectos:

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
fuente