Existe uma diferença de execução entre uma condição JOIN e uma condição WHERE?

17

Existe uma diferença de desempenho entre essas duas consultas de exemplo?

Consulta 1:

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y'

Consulta 2;

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
   and b.tag = 'Y'

Observe que a única diferença é a colocação da condição suplementar; o primeiro usa uma WHEREcláusula e o segundo adiciona a condição à ONcláusula.

Quando executo essas consultas no meu sistema Teradata, os planos de explicação são idênticos e a etapa JOIN mostra a condição adicional em cada caso. No entanto, nesta questão do SO referente ao MySQL, uma das respostas sugeriu que o segundo estilo é preferido porque o WHEREprocessamento ocorre após as junções.

Existe uma regra geral a seguir ao codificar consultas como esta? Acho que deve ser dependente da plataforma, pois obviamente não faz diferença no meu banco de dados, mas talvez isso seja apenas um recurso do Teradata. E se for dependente da plataforma, eu gostaria muito de obter algumas referências de documentação; Eu realmente não sei o que procurar.

BellevueBob
fonte
9
Depende da plataforma, pois depende de como o otimizador RDBMSes lida com a análise e otimização.
Philᵀᴹ
8
E essa resposta na pergunta vinculada merece vários votos negativos. Até o otimizador primitivo do MySQL entenderia que essas consultas simples são equivalentes e que "a cláusula WHERE é avaliada depois que todas as junções foram feitas" é verdadeira apenas em um nível lógico, não na execução real.
ypercubeᵀᴹ
1
Não é realmente uma duplicata; essa pergunta e as respostas estavam comparando a sintaxe JOIN "implícita" versus "explícita". Estou perguntando especificamente sobre condições de junção suplementares.
BellevueBob
Não vou ousar postar uma resposta como eu tentei antes e recebi muitos votos negativos. Quando há muitas junções, tenho casos de experiência em trazer a condição para a junção, resultando em um plano de consulta melhor (filtrado antecipadamente). Ainda os mesmos resultados.
Paparazzo

Respostas:

14

De acordo com o Capítulo 9 (Analisador e Otimizador), Página 172 do Livro Entendendo o MySQL Internals por Sasha Pachev

Entendendo o MySQL Internals

aqui está o detalhamento da avaliação de uma consulta como as seguintes tarefas:

  • Determine quais chaves podem ser usadas para recuperar os registros das tabelas e escolha a melhor para cada tabela.
  • Para cada tabela, decida se uma varredura de tabela é melhor do que a leitura em uma tecla. Se houver muitos registros correspondentes ao valor da chave, as vantagens da chave serão reduzidas e a verificação da tabela se tornará mais rápida.
  • Determine a ordem na qual as tabelas devem ser unidas quando mais de uma tabela estiver presente na consulta.
  • Reescreva as cláusulas WHERE para eliminar o código morto, reduzindo os cálculos desnecessários e alterando as restrições sempre que possível para abrir o caminho para o uso de chaves.
  • Eliminar tabelas não utilizadas da associação.
  • Determine se as chaves podem ser usadas para ORDER BYe GROUP BY.
  • Tente simplificar subconsultas e determinar até que ponto seus resultados podem ser armazenados em cache.
  • Mesclar vistas (expanda a referência da vista como uma macro)

Na mesma página, diz o seguinte:

Na terminologia do otimizador do MySQL, toda consulta é um conjunto de junções. O termo junção é usado aqui mais amplamente do que nos comandos SQL. Uma consulta em apenas uma tabela é uma junção degenerada. Embora normalmente não pensemos em ler registros de uma tabela como uma junção, as mesmas estruturas e algoritmos usados ​​com junções convencionais funcionam perfeitamente para resolver a consulta com apenas uma tabela.

EPÍLOGO

Por causa das chaves presentes, da quantidade de dados e da expressão da consulta, o MySQL Joins às vezes pode fazer coisas para o nosso próprio bem (ou para nos vingar) e apresentar resultados que não esperávamos e que não podemos explicar rapidamente.

Eu escrevi sobre essa estranheza antes

porque o MySQL Query Optimizer poderia dispensar certas chaves durante a avaliação da consulta.

O comentário de @ Phil me ajuda a ver como postar esta resposta (+1 no comentário de @ Phil)

O comentário do @ ypercube (+1 para este também) é uma versão compacta do meu post porque o Query Optimizer do MySQL é primitivo. Infelizmente, tem que ser porque lida com mecanismos de armazenamento externos.

CONCLUSÃO

Quanto à sua pergunta real, o MySQL Query Optimizer determinaria as métricas de desempenho de cada consulta quando for concluída.

  • contando linhas
  • selecionando chaves
  • massageando conjuntos de resultados intermitentes
  • Ah, sim, fazendo o JOIN real

Você provavelmente teria que coagir a ordem de execução reescrevendo (refatorando) a consulta

Aqui está a primeira consulta que você deu

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y';

Tente reescrevê-lo para avaliar o WHERE primeiro

select count(*)
from   table1 a
join   (select key_col from table2 where tag='Y') b
on     b.key_col=a.key_col;

Isso definitivamente alteraria o plano EXPLAIN. Pode produzir resultados melhores ou piores.

Certa vez, respondi a uma pergunta no StackOverflow onde apliquei essa técnica. O EXPLAIN foi horrendo, mas o desempenho foi dinamite. Funcionou apenas devido à presença dos índices corretos e ao uso de LIMIT em uma subconsulta .

Assim como ocorre com os preços das ações, quando se trata de consultas e de tentar expressá-las, aplicam-se restrições, os resultados podem variar e o desempenho passado não é indicativo de resultados futuros.

RolandoMySQLDBA
fonte
2
+1 para obter informações detalhadas específicas do MySQL e, principalmente, para me convencer a aprender a diferença entre "Epílogo" e "Conclusão"!
BellevueBob
No meu post, o Epílogo é uma sub-conclusão.
RolandoMySQLDBA
6
@Rolando: Você pode adicionar um rescaldo sobre melhorias nos otimizadores nas versões mais recentes do MariaDB (5.3 e 5.5) e na versão principal do MySQL (5.6), lançada recentemente. O que pode tornar desnecessárias algumas reescritas.
ypercubeᵀᴹ
1

Para a Oracle, como o mySQL possui uma descrição extensa, temos 2 maneiras de alto nível de alavancar o otimizador.

O primeiro é a Otimização Baseada em Regras (ou RBO). O Oracle possui 15 regras definidas em cada consulta que analisa as tentativas de seguir em uma ordem definida. Se não puder gerar uma consulta otimizada a partir da regra 1, ela avançará para a regra 2 e seguirá em frente até atingir a regra 15.

para obter mais informações: https://docs.oracle.com/cd/B10500_01/server.920/a96533/rbo.htm

Isso afeta os kernels do Oracle RDBMS da 11.1 e abaixo que não foram convertidos no Cost Based Optimizer (também conhecido como CBO). O Oracle 11.2 e posterior requerem o otimizador de CBO, mas podem forçar IDs de SQL específicos a otimizar no método RBO antigo, se o usuário desejar.

Em vez disso, o CBO para Oracle 11.1+ faz vários planos de execução para o mesmo ID SQL e executa o que tem o menor custo total previsto. Ele aproveita grande parte da lógica da RBO, mas analisa as estatísticas da tabela para criar custos dinâmicos do plano de execução para cada operação que o banco de dados precisa realizar para fornecer seus dados ao usuário final. Executar varreduras completas de tabelas em tabelas muito grandes é realmente caro; executar verificações completas de tabela em uma tabela com 10 linhas é barato. Na RBO, essas operações foram consideradas iguais.

para obter mais informações: https://oracle-base.com/articles/misc/cost-based-optimizer-and-database-statistics

Para seu exemplo de consulta específico: a Oracle provavelmente analisará as informações para fazer planos de execução diferentes e, portanto, um será tecnicamente melhor que o outro. No entanto, isso pode ser uma diferença mínima. Observando isso, o Oracle RBO e o CBO gostariam de consultar mais 1 porque está executando em uma junção em menos condições e filtrando uma coluna específica da tabela temporária criada a partir da junção.

JB-Learner
fonte
1

Se você tiver duas consultas e acha que elas são equivalentes, pode acontecer o seguinte:

  1. Ambas as consultas têm o mesmo plano de execução. Isso é bom e é isso que esperamos. Vamos torcer para que seja o plano de execução ideal para a consulta.
  2. existem diferentes planos de execução. Temos duas subcasas aqui.

    2.1 As consultas têm planos de execução diferentes, mas os dois planos apresentam desempenho igualmente bom. Tudo bem também. Não é necessário que, para consultas equivalentes, o mesmo plano seja gerado. Mas o desempenho deve ser igual. E novamente esperamos que seja o melhor possível.

    2.2 As consultas têm planos de execução diferentes e um plano é melhor que o outro. Novamente, temos subcasas:

    2.2.1 Os planos são diferentes porque as consultas não são equivalentes. Portanto, verifique com cuidado se eles são realmente equivalentes. No seu caso, eles são realmente equivalentes.

    2.2.2 Os planos são diferentes, mas as consultas são equivalentes. Isso significa que o otimizador não está maduro o suficiente. Em um mundo perfeito, com otimizadores perfeitos, isso não deve acontecer. Portanto, sim, depende da plataforma e você precisa estudar documentos específicos da plataforma para descobrir por que isso acontece.

    2.2.3 Os planos são diferentes, as consultas são equivalentes, o software do banco de dados possui um bug.

miracle173
fonte