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 quegenerate_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 emBody.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 deLEFT JOIN
em suas subconsultas é inapropriado. Supondo que você esteja usando convenções ANSI paraNULL
comportamento (e você deve ser), qualquer exterior junta-se aoenvelope
retornariaenvelope_command=NULL
, e estes seriam consequentemente excluídos pela condiçãoenvelope_command=?
.
-
Subconsultaso
ei
são quase idênticos, exceto peloenvelope_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.