Básicamente, necesitas una función de ventana. Esa es una característica estándar hoy en día. Además de las funciones de ventana genuinas, puede usar cualquier función agregada como función de ventana en Postgres agregando un OVER
cláusula.
La dificultad especial aquí es obtener las particiones y el orden correcto:
SELECT ea_month, id, amount, ea_year, circle_id
, sum(amount) OVER (PARTITION BY circle_id
ORDER BY ea_year, ea_month) AS cum_amt
FROM tbl
ORDER BY circle_id, month;
Y no GROUP BY
.
La suma de cada fila se calcula desde la primera fila de la partición hasta la fila actual, o citando el manual para ser precisos:
La opción de encuadre predeterminada es RANGE UNBOUNDED PRECEDING
, que es lo mismo que RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Con ORDER BY
, esto establece que el marco sea todas las filas desde el inicio de la partición hasta el último ORDER BY
de la fila actual compañero .
... que es la suma acumulada o acumulada que busca. Énfasis en negrita mío.
Filas con el mismo (circle_id, ea_year, ea_month)
son "compañeros" en esta consulta. Todos ellos muestran la misma suma acumulada con todos los pares agregados a la suma. Pero supongo que su tabla es UNIQUE
en (circle_id, ea_year, ea_month)
, entonces el orden de clasificación es determinista y ninguna fila tiene pares.
Postgres 11 agregó herramientas para incluir/excluir pares con el nuevo frame_exclusion
opciones Ver:
- Agregar todos los valores que no están en el mismo grupo
Ahora, ORDER BY ... ea_month
no funcionará con cadenas para nombres de meses . Postgres ordenaría alfabéticamente según la configuración regional.
Si tiene una date
real valores almacenados en su tabla que puede ordenar correctamente. Si no, sugiero reemplazar ea_year
y ea_month
con una sola columna mon
de tipo date
en tu mesa.
-
Transforma lo que tienes con
to_date()
:to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Para mostrar, puede obtener cadenas originales con
to_char()
:to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Mientras esté atascado con el desafortunado diseño, esto funcionará:
SELECT ea_month, id, amount, ea_year, circle_id
, sum(amount) OVER (PARTITION BY circle_id ORDER BY mon) AS cum_amt
FROM (SELECT *, to_date(ea_year || ea_month, 'YYYYMonth') AS mon FROM tbl)
ORDER BY circle_id, mon;