SQL Server IN vs. desempenho EXISTS

115

Estou curioso para saber qual das opções abaixo seria mais eficiente?

Sempre fui um pouco cauteloso quanto ao uso, INporque acredito que o SQL Server transforma o conjunto de resultados em uma grande IFinstrução. Para um grande conjunto de resultados, isso pode resultar em baixo desempenho. Para pequenos conjuntos de resultados, não tenho certeza se é preferível. Para grandes conjuntos de resultados, não EXISTSseria mais eficiente?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])
Randy Minder
fonte
8
A melhor maneira de descobrir é experimentando e fazendo algumas medições.
Klaus Byskov Pedersen
10
lá está tem que ser um zilhão de duplicatas para esta ......
marc_s
5
@marc_s - Provavelmente sim, mas no tempo que teria levado para olhar todas as postagens sobre o assunto e encontrar uma que se encaixasse no meu caso, tive quatro respostas para minha pergunta.
Randy Minder
7
FYI se você está querendo o mais maneira performance, você pode select 1 from Base...em sua where existsdesde que você realmente não se preocupam com os resultados, basta que uma linha realmente existe.
brad
2
@marc_s isso é muito triste, porque parei para olhar as postagens para não adicionar mais lixo ao stackoverflow. Não preciso de uma resposta personalizada para fazer meu trabalho. Esse é o tipo de pensamento que adicionou uma duplicata Gazillion no lugar de apenas algumas com boas respostas
IvoC

Respostas:

140

EXISTS será mais rápido porque, assim que o motor encontrar um acerto, parará de ver, pois a condição se revelou verdadeira.

Com IN, ele coletará todos os resultados da subconsulta antes do processamento posterior.

keithwarren7
fonte
4
Este é um bom ponto. A instrução IN exige que o SQL Server gere um conjunto de resultados completo e, em seguida, crie uma grande instrução IF, eu acho.
Randy Minder
72
Isso costumava ser verdade, mas nas versões atuais (pelo menos 2008) o otimizador é muito mais inteligente ... na verdade, trata IN () como um EXISTS ().
Aaron Bertrand
11
@Aaron - sim, normalmente o otimizador produzirá internamente um plano melhor. No entanto, confiar em atalhos internos pode ser prejudicial em cenários mais complexos.
Scott Coates
2
Isso é simplesmente errado. Foi em 2010 e ainda é.
Magnus
2
IN e EXISTS têm exatamente o mesmo plano de consulta e IO. Não há razão para pensar que eles são diferentes no desempenho. verifique suas estatísticas de tempo e comprove-se
Nelssen,
40

A resposta aceita é míope e a pergunta um pouco vaga, pois:

1) Não mencione explicitamente se um índice de cobertura está presente à esquerda, à direita ou em ambos os lados.

2) Nenhum dos dois leva em consideração o tamanho do conjunto de entrada do lado esquerdo e do conjunto de entrada do lado direito.
(A questão menciona apenas um grande conjunto de resultados geral ).

Eu acredito que o otimizador é inteligente o suficiente para converter entre "em" vs "existe" quando há uma diferença de custo significativa devido a (1) e (2), caso contrário, pode ser usado apenas como uma dica (por exemplo, existe para encorajar o uso de um índice procurável no lado direito).

Ambos os formulários podem ser convertidos em formulários de junção internamente, ter a ordem de junção invertida e executar como loop, hash ou mesclagem - com base nas contagens de linhas estimadas (esquerda e direita) e na existência de índice à esquerda, à direita ou em ambos os lados.

crokusek
fonte
3
Não sei por que essa excelente resposta não tem recebido mais atenção. Compreender o índice / estrutura de ambos os lados pode impactar, eu concordo. Bem dito.
SheldonH
O otimizador sempre dá o mesmo plano para INe EXISTS. Tente encontrar qualquer caso em que eles não obtenham o mesmo plano (embora isso não se aplique a NOT INe NOT EXISTS)
Martin Smith
@MartinSmith Presumo que você saiba do que está falando, mas você tem alguma prova de que os planos são sempre os mesmos? Nesse caso, isso esclareceria a discordância de uma década aqui.
MarredCheese
@MarredCheese - a responsabilidade recai sobre as pessoas que afirmam que é diferente produzir um único exemplo disso
Martin Smith
37

Eu fiz alguns testes no SQL Server 2005 e 2008, e tanto no EXISTS quanto no IN voltaram com exatamente o mesmo plano de execução real, como outros declararam. O Otimizador é ótimo. :)

Porém, deve-se estar ciente de que EXISTS, IN e JOIN às vezes podem retornar resultados diferentes se você não formular sua consulta corretamente: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx

Adam Nofsinger
fonte
5

Há muitas respostas enganosas aqui, incluindo a altamente votada (embora eu não acredite que suas operações tenham feito mal). A resposta curta é: são iguais.

Existem muitas palavras-chave na linguagem (T-) SQL, mas no final, a única coisa que realmente acontece no hardware são as operações vistas no plano de consulta de execução.

A operação relacional (teoria matemática) que fazemos quando invocamos [NOT] INe [NOT] EXISTSé a semi join (anti-join ao usar NOT). Não é uma coincidência que as operações correspondentes do sql-server tenham o mesmo nome . Não há nenhuma operação que mencione INou em EXISTSqualquer lugar - apenas (anti) semi-junções. Assim, não há como uma escolha logicamente equivalente INvs EXISTSafetar o desempenho porque há uma e única maneira, a operação de execução de (anti) semi-junção, de obter seus resultados .

Um exemplo:

Consulta 1 ( plano )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Consulta 2 ( plano )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)
George Menoutis
fonte
Você já testou? Se sim, você pode compartilhar seu SQL e seus resultados?
UnhandledExcepSean
Testei várias vezes. Posso criar outro caso de teste, e farei isso, mas um caso de teste não significa que o otimizador fará exatamente o mesmo plano em tabelas com estatísticas diferentes. Isso pode levar alguém a pensar que a resposta é parcial - mas a inexistência de vários operadores semijoin é um fato. Talvez eu encontre uma lista em algum lugar e a vincule.
George Menoutis
5

Eu iria com EXISTS em vez de IN, veja o link abaixo:

SQL Server: JOIN vs IN vs EXISTS - a diferença lógica

Existe um equívoco comum de que IN se comporta igualmente a EXISTS ou JOIN em termos de resultados retornados. Isto simplesmente não é verdade.

IN: Retorna verdadeiro se um valor especificado corresponder a qualquer valor em uma subconsulta ou lista.

Existe: Retorna verdadeiro se uma subconsulta contiver alguma linha.

Unir: une 2 conjuntos de resultados na coluna de união.

Crédito do blog: https://stackoverflow.com/users/31345/mladen-prajdic

Curtidor
fonte
Uau, obrigado pelo seu blog e explicação.
Christian Müller
3

Os planos de execução normalmente serão idênticos nesses casos, mas até que você veja como o otimizador influencia em todos os outros aspectos dos índices etc., você realmente nunca saberá.

Cade Roux
fonte
3

Portanto, IN não é igual a EXISTS nem produzirá o mesmo plano de execução.

Normalmente EXISTS é usado em uma subconsulta correlacionada, o que significa que você irá JUNTAR a consulta interna EXISTS à sua consulta externa. Isso adicionará mais etapas para produzir um resultado, pois você precisa resolver as junções de consulta externa e as junções de consulta interna e, em seguida, corresponder suas cláusulas where para unir ambas.

Normalmente IN é usado sem correlacionar a consulta interna com a consulta externa, e isso pode ser resolvido em apenas uma etapa (na melhor das hipóteses).

Considere isto:

  1. Se você usar IN e o resultado da consulta interna for milhões de linhas de valores distintos, provavelmente terá um desempenho MAIS LENTO do que EXISTS, visto que a consulta EXISTS tem desempenho (tem os índices corretos para se juntar à consulta externa).

  2. Se você usar EXISTS e a junção com sua consulta externa for complexa (leva mais tempo para ser executada, nenhum índice adequado), ela tornará a consulta mais lenta pelo número de linhas na tabela externa; às vezes, o tempo estimado para conclusão pode ser em dias. Se o número de linhas for aceitável para o hardware fornecido ou se a cardinalidade dos dados estiver correta (por exemplo, menos valores DISTINCT em um conjunto de dados grande) IN pode ser executado mais rápido do que EXISTS.

  3. Todos os itens acima serão observados quando você tiver uma quantidade razoável de linhas em cada tabela (por justo, quero dizer algo que excede o processamento da CPU e / ou limites de memória ram para cache).

Portanto, a RESPOSTA é DEPENDE. Você pode escrever uma consulta complexa dentro de IN ou EXISTS, mas como regra geral, você deve tentar usar IN com um conjunto limitado de valores distintos e EXISTS quando você tem muitas linhas com muitos valores distintos.

O truque é limitar o número de linhas a serem verificadas.

Saudações,

MarianoC

MarianoC
fonte
1

Para otimizar o EXISTS, seja muito literal; algo precisa estar lá, mas você não precisa realmente de nenhum dado retornado da subconsulta correlacionada. Você está apenas avaliando uma condição booleana.

Assim:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Como a subconsulta correlacionada é RBAR, a primeira ocorrência do resultado torna a condição verdadeira e ela não é mais processada.

Josh Lewis
fonte
Eu sempre seria extremamente cauteloso ao usar a codificação LEFT JOIN + NULL, porque é muito fácil perder ou distorcer os resultados se você não for muito cuidadoso no manuseio de NULL. Raramente encontrei uma situação em que EXISTS ou um CTE (para encontrar duplicação ou inserção sintética para dados ausentes) não atendam aos mesmos requisitos e superassem LEFT JOIN + NULL
Josh Lewis
3
TOP 1 deve ser totalmente estranho (ou redundante de evento) quando usado com EXISTS. EXISTS sempre retorna assim que encontra qualquer linha correspondente.
Karl Kieninger
Não vi nenhum benefício de desempenho com essa abordagem até agora. Por favor, mostre algumas imagens dos Planos de Execução
DaFi4 de
-1

Em cima da minha cabeça e sem garantia de estar correto: acredito que o segundo será mais rápido neste caso.

  1. No primeiro, a subconsulta correlacionada provavelmente fará com que a subconsulta seja executada para cada linha.
  2. No segundo exemplo, a subconsulta deve ser executada apenas uma vez, já que não está correlacionada.
  3. No segundo exemplo, o INentrará em curto-circuito assim que encontrar uma correspondência.
RedFilter
fonte