Subconsultas vs junções

158

Refatorei uma seção lenta de um aplicativo que herdamos de outra empresa para usar uma junção interna em vez de uma subconsulta como:

WHERE id IN (SELECT id FROM ...)

A consulta refatorada é executada 100x mais rápido. (~ 50 segundos para ~ 0,3) Eu esperava uma melhoria, mas alguém pode explicar por que foi tão drástico? As colunas usadas na cláusula where foram todas indexadas. O SQL executa a consulta na cláusula where uma vez por linha ou algo assim?

Atualização - Explique os resultados:

A diferença está na segunda parte da consulta "where id in ()" -

2   DEPENDENT SUBQUERY  submission_tags ref st_tag_id   st_tag_id   4   const   2966    Using where

vs 1 linha indexada com a junção:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index
Palmsey
fonte
4
Possível duplicata da junção vs. subconsulta
Ciro Santilli
2
Não é uma duplicata. Esta pergunta é especificamente sobre encontrar uma diferença de desempenho. A outra pergunta é mais geral, aberta sobre os prós e contras de cada abordagem e por que uma abordagem parece mais popular.
Basil Bourque
@simhumileco Isso não melhora, não faz diferença, é contrário ao que o autor escreveu e esse tipo de edição para o estilo do código é inadequado. Quando devo fazer edições no código?
31519 philipxy
Oi @ philipxy, não pretendia interferir no pensamento do autor, mas apenas para tornar o fragmento do código mais legível e escrito com mais cuidado.
simhumileco 12/07/19

Respostas:

160

Uma "subconsulta correlacionada" (ou seja, aquela na qual a condição where depende dos valores obtidos das linhas da consulta que contém) será executada uma vez para cada linha. Uma subconsulta não correlacionada (aquela na qual a condição where é independente da consulta que contém) será executada uma vez no início. O mecanismo SQL faz essa distinção automaticamente.

Mas, sim, o plano de explicações fornecerá os detalhes sujos.

Jeffrey L Whitledge
fonte
3
Observe que DEPENDENT SUBQUERYsignifica exatamente a mesma coisa que "subconsulta correlacionada".
Timo
38

Você está executando a subconsulta uma vez para cada linha, enquanto a junção acontece nos índices.

Sklivvz
fonte
5
Eu não acho que isso seja verdade. O mecanismo SQL deve executar a subconsulta apenas uma vez e usar o resultado como uma lista.
dacracot 26/09/08
8
Isso depende - se a subconsulta estiver correlacionada de alguma forma com a consulta externa (usa seus dados), ela é executada com cada linha.
qbeuek 26/09/08
4
Provavelmente é verdade neste caso, mas não é verdade em geral.
Amy B
1
O OP EXPLAINdiz DEPENDENT SUBQUERY, que é o indicador mais claro desse comportamento.
Timo
16

Aqui está um exemplo de como as subconsultas são avaliadas no MySQL 6.0 .

O novo otimizador converterá esse tipo de subconsulta em junções.

Giuseppe Maxia
fonte
Isso é um grande artigo sobre o MySQL 6.0 melhorada otimizador, graças
Fogo Corvo
7

Execute o plano de explicação em cada versão, ele mostrará o porquê.

scotta
fonte
6

antes que as consultas sejam executadas no conjunto de dados, elas são colocadas por meio de um otimizador de consultas, o otimizador tenta organizar a consulta de maneira que possa remover o máximo de tuplas (linhas) do conjunto de resultados o mais rápido possível. Freqüentemente, quando você usa subconsultas (especialmente más), as tuplas não podem ser removidas do conjunto de resultados até que a consulta externa comece a ser executada.

Sem ver a consulta, é difícil dizer o que há de tão ruim no original, mas meu palpite seria que era algo que o otimizador simplesmente não poderia melhorar muito. A execução de 'explicação' mostrará o método de otimização para recuperar os dados.

pfranza
fonte
4

Veja o plano de consulta para cada consulta.

Onde in e Join geralmente podem ser implementados usando o mesmo plano de execução; portanto, normalmente não há aceleração zero entre as mudanças.

Amy B
fonte
3
Haha, eu <3 sql esfrega esse voto negativo porque eles não sabem ler planos de consulta.
Amy B
4

O Optimizer não fez um trabalho muito bom. Geralmente eles podem ser transformados sem nenhuma diferença e o otimizador pode fazer isso.

Cade Roux
fonte
4

Geralmente, é o resultado do otimizador não conseguir descobrir que a subconsulta pode ser executada como uma junção; nesse caso, executa a subconsulta para cada registro da tabela, em vez de associá-la à subconsulta na tabela que você está consultando. Alguns dos bancos de dados mais "corporativos" são melhores nisso, mas às vezes ainda perdem isso.

Mark Roddy
fonte
4

Esta pergunta é um tanto geral, então aqui está uma resposta geral:

Basicamente, as consultas demoram mais quando o MySQL tem toneladas de linhas para classificar.

Faça isso:

Execute um EXPLAIN em cada uma das consultas (a que se juntou e depois a subconsultada) e publique os resultados aqui.

Eu acho que ver a diferença na interpretação do MySQL dessas consultas seria uma experiência de aprendizado para todos.

Pete Karl II
fonte
4

A subconsulta where deve executar 1 consulta para cada linha retornada. A junção interna apenas precisa executar 1 consulta.

Shawn
fonte
3

A subconsulta provavelmente estava executando uma "verificação completa da tabela". Em outras palavras, não usando o índice e retornando muitas linhas que a Where da consulta principal estava precisando filtrar.

Apenas um palpite sem detalhes, é claro, mas essa é a situação comum.

igelkott
fonte
2

Com uma subconsulta, você deve executar novamente o 2º SELECT para cada resultado, e cada execução normalmente retorna 1 linha.

Com uma junção, o segundo SELECT retorna muito mais linhas, mas você só precisa executá-lo uma vez. A vantagem é que agora você pode se unir aos resultados, e unir relações é o que um banco de dados deve ser bom. Por exemplo, talvez o otimizador possa identificar como aproveitar melhor um índice agora.

Joel Coehoorn
fonte
2

Não é tanto a subconsulta quanto a cláusula IN, embora as junções estejam na base do mecanismo SQL do Oracle e sejam executadas extremamente rapidamente.

dacracot
fonte
1
onde realmente não é inerentemente ruim.
Shawn
2

Retirado do Manual de Referência ( 14.2.10.11 Reescrevendo subconsultas como junções ):

Uma junção [ESQUERDA] ESQUERDA pode ser mais rápida que uma subconsulta equivalente, porque o servidor pode otimizar melhor - um fato que não é específico apenas ao MySQL Server.

Portanto, as subconsultas podem ser mais lentas que LEFT [OUTER] JOINS.

simhumileco
fonte