PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Otimizando várias junções


Há sempre 2 coisas a considerar ao otimizar consultas:
  • Quais índices podem ser usados ​​(talvez seja necessário criar índices)
  • Como a consulta é escrita (talvez seja necessário alterar a consulta para permitir que o otimizador de consulta seja capaz de encontrar índices apropriados e não reler os dados de forma redundante)

Algumas observações:

  • Você está realizando manipulações de datas antes de ingressar em suas datas. Como regra geral, isso impedirá que um otimizador de consulta use um índice, mesmo que ele exista. Você deve tentar escrever suas expressões de forma que as colunas indexadas existam inalteradas em um lado da expressão.

  • Suas subconsultas estão filtrando para o mesmo intervalo de datas que generate_series . Isso é uma duplicação e limita a capacidade do otimizador de escolher a otimização mais eficiente. Suspeito que isso possa ter sido escrito para melhorar o desempenho porque o otimizador não conseguiu usar um índice na coluna de data (body_time )?

  • OBSERVAÇÃO :Na verdade, gostaríamos muito de usar um índice em Body.body_time

  • ORDER BY dentro das subconsultas é, na melhor das hipóteses, redundante. Na pior das hipóteses, poderia forçar o otimizador de consulta a classificar o conjunto de resultados antes de ingressar; e isso não é necessariamente bom para o plano de consulta. Em vez disso, apenas aplique o pedido no final para exibição final.

  • Uso de LEFT JOIN em suas subconsultas é inapropriado. Supondo que você esteja usando convenções ANSI para NULL comportamento (e você deve ser), qualquer exterior junta-se ao envelope retornaria envelope_command=NULL , e estes seriam consequentemente excluídos pela condição envelope_command=? .

  • Subconsultas o e i são quase idênticos, exceto pelo envelope_command valor. Isso força o otimizador a varrer as mesmas tabelas subjacentes duas vezes. Você pode usar uma tabela dinâmica técnica para unir os dados uma vez e dividir os valores em 2 colunas.

Tente o seguinte, que usa a técnica de pivô:
SELECT  p.period,
        /*The pivot technique in action...*/
        SUM(
        CASE WHEN envelope_command = 1 THEN body_size
        ELSE 0
        END) AS Outbound,
        SUM(
        CASE WHEN envelope_command = 2 THEN body_size
        ELSE 0
        END) AS Inbound
FROM    (
        SELECT  date '2009-10-01' + s.day AS period
        FROM    generate_series(0, date '2009-10-31' - date '2009-10-01') AS s(day)
        ) AS p 
        /*The left JOIN is justified to ensure ALL generated dates are returned
          Also: it joins to a subquery, else the JOIN to envelope _could_ exclude some generated dates*/
        LEFT OUTER JOIN (
        SELECT  b.body_size,
                b.body_time,
                e.envelope_command
        FROM    body AS b 
                INNER JOIN envelope e 
                  ON e.message_id = b.message_id 
        WHERE   envelope_command IN (1, 2)
        ) d
          /*The expressions below allow the optimser to use an index on body_time if 
            the statistics indicate it would be beneficial*/
          ON d.body_time >= p.period
         AND d.body_time < p.period + INTERVAL '1 DAY'
GROUP BY p.Period
ORDER BY p.Period

EDITAR :Adicionado filtro sugerido por Tom H.