Qual é a diferença entre NOT EXISTS vs. NOT IN vs. LEFT JOIN ON WHERE IS NULL?

151

Parece-me que você pode fazer a mesma coisa em uma consulta SQL usando NOT EXISTS, NOT IN ou LEFT JOIN WHERE IS NULL. Por exemplo:

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

Não tenho certeza se tenho toda a sintaxe correta, mas estas são as técnicas gerais que já vi. Por que eu escolheria usar um sobre o outro? O desempenho difere ...? Qual destes é o mais rápido / mais eficiente? (Se depender da implementação, quando eu usaria cada um?)

froadie
fonte
6
Muitos mecanismos SQL comuns permitem ver um plano de execução. Muitas vezes, é possível detectar diferenças significativas na eficiência para consultas logicamente equivalentes dessa maneira. O sucesso de qualquer método depende de fatores como tamanho da tabela, quais índices estão presentes e outros.
Chris Farmer
2
@wich: nenhum banco de dados se importa com o que exatamente você retorna dentro da EXISTScláusula. Você pode retornar *, NULLou o que for: tudo isso será otimizado.
Quassnoi
2
@wich - por quê? Tanto aqui: techonthenet.com/sql/exists.php e aqui: msdn.microsoft.com/en-us/library/ms188336.aspx parecem usar * ...
froadie
8
@wich: não se trata de "manifestar interesse". É sobre o analisador de consultas que você deve colocar algo entre SELECTe FROM. E *é apenas mais fácil digitar. Sim, SQLtem alguma semelhança com uma linguagem natural, mas é analisada e executada por uma máquina, uma máquina programada. Não é que ele de repente entre no seu cubículo e grite "pare de exigir os campos extras em uma EXISTSconsulta, porque eu estou cansado de analisá-los e depois jogá-los fora!". Está tudo bem com um computador, realmente.
Quassnoi 12/02/10
1
@Quassnoi, se você escrevesse código com o único objetivo de uma máquina interpretá-lo, o código ficaria horrível e, infelizmente, muitas pessoas trabalham assim. Se, no entanto, você escrever código em outra ótica, escrevendo código para expressar o que deseja que a máquina faça como um comunicado aos seus colegas, você escreverá um código melhor e mais sustentável. Seja esperto, escreva código para as pessoas, não para o computador.
que

Respostas:

139

Em poucas palavras:

NOT INé um pouco diferente: nunca corresponde se houver apenas um NULLna lista.

  • Em MySQL, NOT EXISTSé um pouco menos eficiente

  • Em SQL Server, LEFT JOIN / IS NULLé menos eficiente

  • Em PostgreSQL, NOT INé menos eficiente

  • Em Oracle, todos os três métodos são os mesmos.

Quassnoi
fonte
1
Obrigado pelos links! E obrigado pela rápida visão geral ... Meu escritório está bloqueando o link por algum motivo: P, mas vou dar uma olhada assim que chegar a um computador comum.
Febrie
2
Outro ponto é que, se table1 .acontiver, NULLa EXISTSconsulta não retornará esta linha, mas a NOT INconsulta se table2estiver vazia. NOT IN vs. NOT EXISTS Colunas anuláveis: SQL Server
Martin Smith
@ MartinSmith: NULL NOT IN ()avalia como verdadeiro (não NULL), assim comoNOT EXISTS (NULL = column)
Quassnoi 19/06/12
2
@ Quassnoi - er, bom ponto, entendi errado. O NOT EXISTSsempre retornará a linha, mas NOT INsomente o fará se a subconsulta não retornar nenhuma linha.
Martin Smith
5

Se o banco de dados é bom para otimizar a consulta, os dois primeiros serão transformados em algo próximo ao terceiro.

Para situações simples como as que você pergunta, deve haver pouca ou nenhuma diferença, pois todas serão executadas como junções. Em consultas mais complexas, o banco de dados pode não conseguir fazer uma junção das consultas not ine not exists. Nesse caso, as consultas ficarão muito mais lentas. Por outro lado, uma associação também pode ter um desempenho ruim se não houver um índice que possa ser usado; portanto, apenas porque você usa uma associação não significa que você está seguro. Você precisaria examinar o plano de execução da consulta para saber se pode haver algum problema de desempenho.

Guffa
fonte
2

Supondo que você esteja evitando nulos, são todas as formas de escrever uma anti-junção usando o SQL padrão.

Uma omissão óbvia é o equivalente usando EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Observe no Oracle que você precisa usar o MINUSoperador (sem dúvida um nome melhor):

SELECT a FROM table1
MINUS
SELECT a FROM table2

Falando em sintaxe proprietária, também pode haver equivalentes não-padrão que valem a pena investigar, dependendo do produto que você está usando, por exemplo, OUTER APPLYno SQL Server (algo como):

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;
um dia quando
fonte
0

Quando for necessário inserir dados na tabela com chave primária de vários campos, considere que será muito mais rápido (tentei no Access, mas acho que em qualquer banco de dados) não verificar se "não existem registros com 'tais' valores na tabela", - basta inserir na tabela e os registros em excesso (pela tecla) não serão inseridos duas vezes.

baleks
fonte
0

A perspectiva de desempenho sempre evita o uso de palavras-chave inversas como NOT IN, NOT EXISTS, ... Porque para verificar os itens inversos, o DBMS precisa executar todas as opções disponíveis e soltar a seleção inversa.

Lahiru Cooray
fonte
1
E o que você propõe como solução alternativa quando realmente precisa NOT?
Dnoeth 6/04/16
Bem, quando não há opção de causa, precisamos usar operações NOT e é por isso que elas existem. A melhor prática é evitá-los quando tivermos outras soluções alternativas.
Lahiru Cooray
@onedaywhen, se um otimizador transforma uma consulta e retorna o resultado errado , então é um bug
David דודו Markovitz
@DuduMarkovitz: sim, e se você entrar em contato com a equipe do SQL Server e eles reconhecerem o erro, mas se recusarem a corrigi-lo, porque eles afirmam que isso pode fazer com que as consultas sejam mais lentas, é um problema com o qual você precisa lidar .
onedaywhen
@onedaywhen - Este não era um cenário hipotético, presumo :-) Você se lembra dos detalhes do bug?
David Markovitz 10/10