Consulta MySQL “NOT IN”

181

Eu queria executar uma consulta simples para exibir todas as linhas em Table1que um valor da coluna principal não está presente em uma coluna em outra tabela ( Table2).

Eu tentei usar:

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

Em vez disso, está lançando um erro de sintaxe. A pesquisa no Google me levou a fóruns onde as pessoas diziam que o MySQL não suporta NOT INe algo extremamente complexo precisa ser usado. Isso é verdade? Ou estou cometendo um erro horrendo?

Kshitij Saxena -KJ-
fonte
1
E se eu quiser dados semelhantes de três tabelas. Quero dizer, uma tabela1 tem 2000 entradas, as outras duas tabelas 2 e 3 dizem cada uma 500 entradas, todas elas têm o campo 'nome' comum. Como podemos obter todos os detalhes da tabela 1 que não estão presentes nas tabelas 2 e 3 com base em 'nome'. Podemos usar NOT IN duas vezes, se sim, como ..?

Respostas:

310

Para usar IN, você deve ter um conjunto, use esta sintaxe:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
Julien Lebosquain
fonte
85
Cuidado quando table2.principalpode ser NULL. Nesse caso NOT IN, sempre retornará FALSEporque NOT INé tratado como <> ALL, que compara todas as linhas da subconsulta como Table1.principal <> table2.principal, que falha ao comparar com NULL: Table1.principal <> NULLnão resultará em TRUE. Para corrigir: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti
4
Obrigado pelo comentário @Basti! Passei muito tempo tentando entender por que a consulta não estava funcionando conforme o esperado.
gvas 23/03/19
3
Não se esqueça de evitar o uso de 'SELECT *' dentro da lista 'NOT IN'. Você deve escolher uma coluna específica. Caso contrário, você receberá este erro: stackoverflow.com/questions/14046838/…
Lorien Brune
165

A opção de subconsulta já foi respondida, mas observe que em muitos casos a LEFT JOINpode ser uma maneira mais rápida de fazer isso:

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

Se você deseja verificar várias tabelas para garantir que ela não esteja presente em nenhuma das tabelas (como no comentário do SRKR), você pode usar o seguinte:

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ý
fonte
2
Em meus próprios testes, tinha mesmo desempenho tanto para NOT IN& LEFT JOIN. +1 em ambos
BufferStack
uma vez que a RAN consulta uma vez que você deve obter os mesmos resultados, não importa o que devido ao cache DB interna
Toote
Para mim, o desempenho foi muito melhor. Corri por tabelas diferentes, enquanto elas tinham chaves estrangeiras.
Keenora Fluffball
36

NOT IN vs. NOT EXISTS vs. LEFT JOIN / É NULL no MySQL

O MySQL, assim como todos os outros sistemas, exceto o SQL Server, pode otimizar LEFT JOIN/IS NULL retornar FALSEassim que o valor correspondente for encontrado, e é o único sistema que se preocupou em documentar esse comportamento. […] Como o MySQL não é capaz de usar HASHe MERGEjuntar algoritmos, o único que ANTI JOINele é capaz é oNESTED LOOPS ANTI JOIN

[...]

Essencialmente, [ NOT IN] é exatamente o mesmo plano que LEFT JOIN/ IS NULLusa, apesar de esses planos serem executados pelas diferentes ramificações do código e parecerem diferentes nos resultados de EXPLAIN. Os algoritmos são de fato os mesmos e as consultas são concluídas ao mesmo tempo.

[...]

É difícil dizer o motivo exato de [queda de desempenho ao usar NOT EXISTS] , pois essa queda é linear e não parece depender da distribuição de dados, número de valores nas duas tabelas etc., desde que os dois campos sejam indexados. Como existem três partes de código no MySQL que essencialmente fazem um trabalho, é possível que o código responsável EXISTSfaça algum tipo de verificação extra, o que leva mais tempo.

[...]

O MySQL pode otimizar todos os três métodos para fazer uma espécie de NESTED LOOPS ANTI JOIN. […] No entanto, esses três métodos geram três planos diferentes, executados por três partes diferentes de código. O código que executa o EXISTSpredicado é cerca de 30% menos eficiente […]

É por isso que a melhor maneira de procurar valores ausentes no MySQL é usando um LEFT JOIN/ IS NULLou NOT INmelhor que NOT EXISTS.

(ênfases adicionadas)

engin
fonte
7

Infelizmente, parece haver um problema com o uso do MySQL da cláusula "NOT IN", a captura de tela abaixo mostra a opção de subconsulta retornando resultados incorretos:

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
fonte
7

Cuidado NOT INnão é um apelido para <> ANY, mas para <> 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

não pode ser substituído por

SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)

Você deve usar

SELECT c FROM t1 WHERE c <> ANY (SELECT c FROM t2)
user4554358
fonte