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

Suma acumulada de valores por mes, completando los meses que faltan

Esto es muy similar a otras preguntas, pero la mejor consulta sigue siendo complicada.

Consulta básica para obtener la suma acumulada rápidamente:

SELECT to_char(date_trunc('month', date_added), 'Mon YYYY') AS mon_text
     , sum(sum(qty)) OVER (ORDER BY date_trunc('month', date_added)) AS running_sum
FROM   tbl
GROUP  BY date_trunc('month', date_added)
ORDER  BY date_trunc('month', date_added);

La parte complicada es rellenar los meses que faltan :

WITH cte AS (
   SELECT date_trunc('month', date_added) AS mon, sum(qty) AS mon_sum
   FROM   tbl
   GROUP  BY 1
   )
SELECT to_char(mon, 'Mon YYYY') AS mon_text
     , sum(c.mon_sum) OVER (ORDER BY mon) AS running_sum
FROM  (SELECT min(mon) AS min_mon FROM cte) init
     , generate_series(init.min_mon, now(), interval '1 month') mon
LEFT   JOIN cte c USING (mon)
ORDER  BY mon;

El implícito CROSS JOIN LATERAL requiere Postgres 9.3+. Esto comienza con el primer mes en la tabla.
Para comenzar con un mes determinado :

WITH cte AS (
   SELECT date_trunc('month', date_added) AS mon, sum(qty) AS mon_sum
   FROM   tbl
   GROUP  BY 1
   )
SELECT to_char(mon, 'Mon YYYY') AS mon_text
     , COALESCE(sum(c.mon_sum) OVER (ORDER BY mon), 0) AS running_sum
FROM   generate_series('2015-01-01'::date, now(), interval '1 month') mon
LEFT   JOIN cte c USING (mon)
ORDER  BY mon;

SQL Fiddle.

Separación de meses de diferentes años. No lo pediste, pero lo más probable es que lo quieras.

Tenga en cuenta que el "mes" hasta cierto punto depende de la configuración de la zona horaria de la sesión actual. Detalles:

Relacionado: