Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

A ordem dos campos em uma cláusula WHERE afeta o desempenho no MySQL?


O SQL foi projetado para ser uma linguagem declarativa, não procedural. Portanto, o otimizador de consulta não considere a ordem dos predicados da cláusula where para determinar como aplicá-los.

Provavelmente vou simplificar demais a discussão a seguir sobre um otimizador de consulta SQL. Eu escrevi um ano atrás, nesse sentido (foi muito divertido!). Se você realmente deseja se aprofundar na otimização de consulta moderna, consulte o SQL Tuning , de O'Reilly.

Em um otimizador de consulta SQL simples, a instrução SQL primeiro é compilada em uma árvore de álgebra relacional operações. Cada uma dessas operações recebe uma ou mais tabelas como entrada e produz outra tabela como saída. Digitalizar é uma varredura sequencial que lê uma tabela do banco de dados. Classificar produz uma tabela ordenada. Selecionar produz uma tabela cujas linhas são selecionadas de outra tabela de acordo com alguma condição de seleção. Projeto produz uma tabela com apenas certas colunas de outra tabela. Produtos cruzados pega duas tabelas e produz uma tabela de saída composta por todos os pares concebíveis de suas linhas.

Confusamente, a cláusula SQL SELECT é compilada em uma álgebra relacional Projeto , enquanto a cláusula WHERE se transforma em uma álgebra relacional Select . A cláusula FROM se transforma em um ou mais Joins , cada um recebendo duas mesas e produzindo uma mesa para fora. Existem outras operações de álgebra relacional envolvendo união de conjuntos, interseção, diferença e associação, mas vamos manter isso simples.

Esta árvore realmente precisa ser otimizada. Por exemplo, se você tiver:
select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

com 5.000 funcionários em 500 departamentos, a execução de uma árvore não otimizada produzirá cegamente todas as combinações possíveis de um funcionário e um departamento (um produto cruzado ) e, em seguida, Selecionar apenas a combinação que era necessária. A digitalização de Funcionário produzirá uma tabela de 5.000 registros, o Scan do Departamento produzirá uma tabela de 500 registros, o Cross Product dessas duas tabelas produzirá uma tabela de 2.500.000 registros, e o Selecionar on E.id pegará essa tabela de 2.500.000 registros e descartará todos, exceto um, o registro desejado.

[Processadores de consulta reais tentarão não materializar todas essas tabelas intermediárias na memória, é claro.]

Assim, o otimizador de consulta percorre a árvore e aplica várias otimizações. Uma é dividir cada Seleção em uma cadeia de Seleções , um para cada um dos Selecionar originais 's condições de nível superior, os e-ed juntos. (Isso é chamado de "forma normal conjuntiva".) Em seguida, o indivíduo menor Seleciona são movidos na árvore e mesclados com outras operações de álgebra relacional para formar operações mais eficientes.

No exemplo acima, o otimizador primeiro pressiona o botão Selecionar em E.id =123456 abaixo do caro Cross Product Operação. Isso significa que o produto cruzado apenas produz 500 linhas (uma para cada combinação desse funcionário e um departamento). Em seguida, o nível superior Selecionar for E.dept_id =D.dept_id filtra as 499 linhas indesejadas. Nada mal.

Se houver um índice no campo id do funcionário, o otimizador poderá combinar o Scan de Funcionário com o Selecionar em E.id =123456 para formar um índice rápido Pesquisa . Isso significa que apenas uma linha Employee é lida na memória do disco em vez de 5.000. Coisas estão melhorando.

A principal otimização final é usar o Selecionar em E.dept_id =D.dept_id e combine-o com o Cross Product . Isso o transforma em uma álgebra relacional Equijoin Operação. Isso não faz muito por si só. Mas se houver um índice em Department.dept_id, o Scan sequencial de nível inferior do Departamento alimentando o Equijoin pode ser transformado em um índice muito rápido Pesquisa do registro do nosso departamento de um funcionário.

Otimizações menores envolvem o envio de Projeto operações para baixo. Se o nível superior da sua consulta precisar apenas de E.name e D.name, e as condições precisarem de E.id, E.dept_id e D.dept_id, então o Scan as operações não precisam construir tabelas intermediárias com todas as outras colunas, economizando espaço durante a execução da consulta. Transformamos uma consulta terrivelmente lenta em duas pesquisas de índice e nada mais.

Indo mais para a pergunta original, digamos que você tenha:
select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

A árvore de álgebra relacional não otimizada, quando executada, faria uma varredura nos 5.000 funcionários e produziria, digamos, os 126 em Delaware com mais de 21 anos. O otimizador de consulta também tem uma ideia aproximada dos valores no banco de dados. Ele pode saber que a coluna E.state tem os 14 estados em que a empresa tem localizações e algo sobre as distribuições E.age. Então, primeiro ele vê se um dos campos está indexado. Se E.state for, faz sentido usar esse índice apenas para selecionar o pequeno número de funcionários que o processador de consulta suspeita estar em Delaware com base em suas últimas estatísticas computadas. Se apenas o E.age for, o processador de consultas provavelmente decidirá que não vale a pena, já que 96% de todos os funcionários têm 22 anos ou mais. Portanto, se E.state estiver indexado, nosso processador de consulta quebra o Select e mescla o E.state ='Delaware' com o Scan para transformá-lo em uma Varredura de Índice muito mais eficiente .

Digamos neste exemplo que não há índices em E.state e E.age. A combinação Selecionar operação ocorre após o "Scan" sequencial do Funcionário. Faz diferença qual condição no Selecionar é feito primeiro? Provavelmente não é um grande negócio. O processador de consultas pode deixá-los na ordem original na instrução SQL ou pode ser um pouco mais sofisticado e observar a despesa esperada. A partir das estatísticas, ele descobriria novamente que a condição E.state ='Delaware' deveria ser mais altamente seletiva, então reverteria as condições e faria isso primeiro, para que houvesse apenas 126 E.age> 21 comparações em vez de 5.000 . Ou pode perceber que as comparações de igualdade de strings são muito mais caras do que comparações de inteiros e deixar a ordem em paz.

De qualquer forma, tudo isso é muito complexo e é muito improvável que sua ordem de condição sintática faça diferença. Eu não me preocuparia com isso, a menos que você tenha um problema real de desempenho e seu fornecedor de banco de dados use a ordem de condição como uma dica.