Siempre hay 2 cosas a considerar al optimizar las consultas:
- Qué índices se pueden usar (es posible que deba crear índices)
- Cómo se escribe la consulta (es posible que deba cambiar la consulta para permitir que el optimizador de consultas pueda encontrar los índices apropiados y no volver a leer los datos de forma redundante)
Algunas observaciones:
-
Está realizando manipulaciones de fechas antes de unirlas. Como regla general, esto evitará que un optimizador de consultas use un índice, incluso si existe. Debe tratar de escribir sus expresiones de tal manera que las columnas indexadas existan inalteradas en un lado de la expresión.
-
Sus subconsultas se filtran al mismo intervalo de fechas que
generate_series
. Esto es una duplicación y limita la capacidad del optimizador para elegir la optimización más eficiente. Sospecho que puede haberse escrito para mejorar el rendimiento porque el optimizador no pudo usar un índice en la columna de fecha (body_time
)? -
NOTA :De hecho, nos gustaría mucho usar un índice en
Body.body_time
-
ORDER BY
dentro de las subconsultas es, en el mejor de los casos, redundante. En el peor de los casos, podría obligar al optimizador de consultas a ordenar el conjunto de resultados antes de unirse; y eso no es necesariamente bueno para el plan de consulta. Más bien, solo aplique el pedido justo al final para la visualización final. -
Uso de
LEFT JOIN
en sus subconsultas es inapropiado. Suponiendo que está utilizando convenciones ANSI paraNULL
comportamiento (y deberías serlo), cualquier externo se une aenvelope
devolveríaenvelope_command=NULL
, y estos serían por lo tanto excluidos por la condiciónenvelope_command=?
. -
Subconsultas
o
yi
son casi idénticos excepto por elenvelope_command
valor. Esto obliga al optimizador a escanear las mismas tablas subyacentes dos veces. Puede usar una tabla dinámica técnica para unir los datos una vez y dividir los valores en 2 columnas.
Pruebe lo siguiente que utiliza la técnica de pivote:
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 :Filtro agregado sugerido por Tom H.