As cláusulas WHERE são aplicadas na ordem em que são escritas?


36

Estou tentando otimizar uma consulta que analisa uma grande tabela (37 milhões de linhas) e tem uma pergunta sobre a ordem em que as operações são executadas em uma consulta.

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

As WHEREcláusulas do período são executadas antes da subconsulta? É uma boa maneira de colocar as cláusulas mais restritivas em primeiro lugar, para evitar grandes loops para outras cláusulas, a fim de executar uma execução mais rápida?

Agora, as consultas levam muito tempo para serem executadas.

Respostas:


68

Para elaborar a resposta da @ alci:

O PostgreSQL não se importa em que ordem você escreve as coisas

  • O PostgreSQL não se importa com a ordem das entradas em uma WHEREcláusula e escolhe índices e ordem de execução com base apenas na estimativa de custo e seletividade.

  • A ordem na qual as junções são gravadas também é ignorada até o configurado join_collapse_limit; se houver mais junções do que isso, elas serão executadas na ordem em que foram gravadas.

  • As subconsultas podem ser executadas antes ou depois da consulta que as contém, dependendo do que for mais rápido, desde que a subconsulta seja executada antes da consulta externa realmente precisar das informações. Freqüentemente, na realidade, a subconsulta é executada meio que no meio ou intercalada com a consulta externa.

  • Não há garantia de que o PostgreSQL realmente execute partes da consulta. Eles podem ser completamente otimizados. Isso é importante se você chamar funções com efeitos colaterais.

O PostgreSQL transformará sua consulta

O PostgreSQL transformará fortemente as consultas, mantendo exatamente os mesmos efeitos, para fazê-las rodar mais rápido sem alterar os resultados.

  • Termos fora de uma subconsulta podem ser empurrados para baixo na subconsulta, para que sejam executados como parte da subconsulta, não onde você os escreveu na consulta externa

  • Os termos na subconsulta podem ser puxados para a consulta externa, para que sua execução seja feita como parte da consulta externa, não onde você os escreveu na subconsulta

  • A subconsulta pode, e geralmente é, achatada em uma junção na tabela externa. O mesmo vale para coisas como EXISTSe NOT EXISTSconsultas.

  • As visualizações são achatadas na consulta que usa a visualização

  • As funções SQL geralmente são incorporadas à consulta de chamada

  • ... e existem inúmeras outras transformações feitas em consultas, como pré-avaliação de expressão constante, desacorrelação de algumas subconsultas e todos os tipos de outros truques do planejador / otimizador.

Em geral, o PostgreSQL pode transformar e reescrever massivamente sua consulta, até o ponto em que cada uma dessas consultas:

select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;

select *
from my_table
where not exists (
  select 1
  from other_table
  where other_table.my_table_id = my_table.id
);

select *
from my_table
where my_table.id not in (
  select my_table_id
  from other_table
  where my_table_id is not null
);

geralmente todos produzem exatamente o mesmo plano de consulta. (Supondo que eu não tenha cometido nenhum erro estúpido acima).

Não é incomum tentar otimizar uma consulta apenas para descobrir que o planejador de consultas já descobriu os truques que você está tentando e os aplicou automaticamente, para que a versão otimizada à mão não seja melhor que a original.

Limitações

O planejador / otimizador está longe de ser insuficiente e é limitado pelo requisito de ter certeza absoluta de que não pode alterar os efeitos da consulta, os dados disponíveis para tomar decisões, as regras implementadas e o tempo da CPU pode gastar pensando nas otimizações. Por exemplo:

  • O planejador conta com as estatísticas mantidas por ANALYZE(geralmente via autovacuum). Se estes estiverem desatualizados, a escolha do plano pode ser ruim.

  • As estatísticas são apenas uma amostra, portanto, podem ser enganosas devido aos efeitos de amostragem, especialmente se uma amostra muito pequena for coletada. Más escolhas de plano podem resultar.

  • As estatísticas não acompanham alguns tipos de dados sobre a tabela, como correlações entre colunas. Isso pode levar o planejador a tomar más decisões quando assume que as coisas são independentes quando não são.

  • O planejador se baseia em parâmetros de custo, como random_page_costa velocidade relativa de várias operações no sistema específico em que está instalado. Estes são apenas guias. Se estiverem muito errados, podem levar a más escolhas de plano.

  • Qualquer subconsulta com LIMITou OFFSETnão pode ser achatada ou sujeita a pull / pushdown. Isso não significa que ele vai executar antes de todas as partes da consulta externa, embora, ou mesmo que ele vai executar em tudo .

  • Os termos CTE (as cláusulas de uma WITHconsulta) são sempre executados em sua totalidade, se são executados. Eles não podem ser achatados e os termos não podem ser empurrados para cima ou para baixo através da barreira de termos do CTE. Os termos CTE são sempre executados antes da consulta final. Esse é um comportamento não padrão do SQL , mas está documentado como o PostgreSQL faz as coisas.

  • O PostgreSQL possui uma capacidade limitada de otimizar consultas em tabelas, security_barriervisualizações e outros tipos especiais de relação

  • O PostgreSQL não incorporará uma função escrita em nada, exceto SQL simples, nem fará pull / pushdown entre ela e a consulta externa.

  • O planejador / otimizador é realmente burro em selecionar índices de expressão e em diferenças triviais de tipo de dados entre índice e expressão.

Toneladas mais também.

Sua pergunta

No caso da sua consulta:

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

nada impede que seja achatado em uma consulta mais simples com um conjunto extra de junções, e provavelmente será.

Provavelmente resultará em algo como (não testado, obviamente):

select 1 
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province  
inner join center cr on cr.id_cr = province.id_cr 
where upper(offer.code_status) <> 'A' 
   and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
   and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
   and day.date_day >= '2014-10-01' 
   and day.date_day <= '2015-09-30';

O PostgreSQL otimizará a ordem de junção e os métodos de junção com base em suas estimativas de seletividade e contagem de linhas e nos índices disponíveis. Se elas refletem razoavelmente a realidade, ele faz as junções e executa as entradas da cláusula where na ordem que for melhor - geralmente as misturando, então faz um pouco disso, depois um pouco daquilo, e volta à primeira parte etc.

Como ver o que o otimizador fez

Você não pode ver o SQL no qual o PostgreSQL otimiza sua consulta, porque ele converte o SQL em uma representação interna da árvore de consultas e modifica isso. Você pode despejar o plano de consulta e compará-lo com outras consultas.

Não há como "separar" esse plano de consulta ou a árvore do plano interno de volta ao SQL.

http://explain.depesz.com/ possui um assistente de plano de consulta decente. Se você é totalmente novo em planos de consulta, etc. (nesse caso, estou surpreso por você ter chegado tão longe neste post), o PgAdmin possui um visualizador de plano de consulta gráfico que fornece muito menos informações, mas é mais simples.

Leitura relacionada:

Os recursos de empilhamento / retirada e nivelamento continuam a melhorar a cada versão . O PostgreSQL geralmente está certo sobre as decisões de pull-up / push-down / flattening, mas nem sempre, então ocasionalmente você precisa (ab) usar um CTE ou o OFFSET 0hack. Se você encontrar esse caso, relate um erro do planejador de consultas.


Se você está realmente interessado, também pode usar a debug_print_plansopção para ver o plano de consulta bruto, mas prometo que não deseja ler isso. Sério.


Uau ... resposta bastante completa :-) Um dos casos em que eu tive planos lentos com o Postgresql (assim como com outros mecanismos de banco de dados conhecidos, como Oracle), é com correlações entre colunas ou várias junções correlacionadas. Muitas vezes, ele acaba fazendo loops aninhados, pensando que há apenas algumas linhas nesse ponto do plano, quando na verdade existem milhares. Uma maneira de otimizar esse tipo de consulta é 'definir enable_nestloop = off;' pela duração da consulta.
alci

Eu me deparei com uma situação em que a v9.5.5 estava tentando aplicar TO_DATE antes da verificação de se poderia ser aplicada, em uma consulta simples da cláusula 7 where. Ordem importava.
user1133275

@ user1133275 Nesse caso, funcionou apenas para você por acaso, porque as estimativas de custo de cálculo eram as mesmas. O PostgreSQL ainda pode decidir executar to_dateantes da verificação em alguma versão posterior ou devido a alguma alteração nas estatísticas do otimizador. Para executar uma verificação de forma confiável antes de uma função que só deve ser executada após a verificação, use uma CASEinstrução
Craig Ringer

uma das maiores respostas que eu vi no SO! Polegares para cima, cara!
62mkv

Experimentei situações em que a simples adição de consultas order bytornava a execução da consulta muito mais rápida do que se não houvesse order by. Essa é uma das razões pelas quais escrevo minhas consultas com junções, como se quisesse executá-las - é bom ter um ótimo otimizador, mas acho que não é aconselhável confiar inteiramente em seu destino no resultado e escrever consultas sem pensar em como. -lo may beexecutado ... Grande resposta !!
precisa saber é o seguinte

17

SQL é uma linguagem declarativa: você diz o que deseja, não como fazê-lo. O RDBMS escolherá a maneira como executará a consulta, chamada de plano de execução.

Uma vez (5 a 10 anos atrás), a maneira como uma consulta foi gravada teve um impacto direto no plano de execução, mas hoje em dia, a maioria dos mecanismos de banco de dados SQL usa um Otimizador Baseado em Custo para o planejamento. Ou seja, ele avaliará diferentes estratégias para executar a consulta, com base em suas estatísticas nos objetos de banco de dados, e escolherá a melhor.

Na maioria das vezes, é realmente o melhor, mas às vezes o mecanismo do DB faz más escolhas, resultando em consultas muito lentas.


Deve-se notar que em alguns RDBMSs a ordem das consultas ainda é significativa, mas para os mais avançados, tudo o que você diz é verdadeiro na prática e também na teoria. Quando o planejador de consultas está escolhendo más opções de ordem de execução, geralmente há dicas de consulta disponíveis para levá-lo a uma direção mais eficiente (como WITH(INDEX(<index>))no MSSQL para forçar a escolha do índice para uma associação específica).
David Spillett

A questão é se date_dayrealmente existe algum índice no . Se não houver, o otimizador não tem muitos planos para comparar.
Jkavalik 22/09/2015
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.