sql >> Base de Datos >  >> RDS >> PostgreSQL

Optimización de múltiples uniones

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 para NULL comportamiento (y deberías serlo), cualquier externo se une a envelope devolvería envelope_command=NULL , y estos serían por lo tanto excluidos por la condición envelope_command=? .

  • Subconsultas o y i son casi idénticos excepto por el envelope_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.