Basicamente, você precisa de uma função de janela. Essa é uma característica padrão hoje em dia. Além das funções de janela genuínas, você pode usar qualquer função agregada como função de janela no Postgres anexando um
OVER
cláusula. A dificuldade especial aqui é obter partições e ordenar corretamente:
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;
E não
GROUP BY
. A soma de cada linha é calculada da primeira linha na partição até a linha atual - ou citando o manual para ser mais preciso:
A opção de enquadramento padrão éRANGE UNBOUNDED PRECEDING
, que é o mesmo queRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. ComORDER BY
, isso define o quadro como todas as linhas da partição iniciam até o últimoORDER BY
da linha atual colega .
... que é a soma acumulada ou corrente que você procura. Minha ênfase em negrito.
Linhas com o mesmo
(circle_id, ea_year, ea_month)
são "pares" nesta consulta. Todos eles mostram a mesma soma corrente com todos os pares adicionados à soma. Mas suponho que sua tabela seja UNIQUE
em (circle_id, ea_year, ea_month)
, a ordem de classificação será determinística e nenhuma linha terá pares. Postgres 11 adicionou ferramentas para incluir/excluir peers com o novo
frame_exclusion
opções. Ver:- Agregando todos os valores que não estão no mesmo grupo
Agora,
ORDER BY ... ea_month
não funcionará com strings para nomes de meses . O Postgres classificaria em ordem alfabética de acordo com a configuração de localidade. Se você tiver
date
real valores armazenados em sua tabela você pode classificar corretamente. Caso contrário, sugiro substituir ea_year
e ea_month
com uma única coluna mon
do tipo date
na sua mesa. -
Transforme o que você tem comto_date()
:
to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Para exibição, você pode obter strings originais comto_char()
:
to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Enquanto estiver preso ao design infeliz, isso 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;