In principe heb je een vensterfunctie nodig. Dat is tegenwoordig standaard. Naast echte vensterfuncties kunt u elke . gebruiken verzamelfunctie als vensterfunctie in Postgres door een OVER
. toe te voegen clausule.
De speciale moeilijkheid hier is om partities en sorteervolgorde goed te krijgen:
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;
En nee GROUP BY
.
De som voor elke rij wordt berekend vanaf de eerste rij in de partitie tot de huidige rij - of om precies te zijn de handleiding citerend:
De standaard framing-optie is RANGE UNBOUNDED PRECEDING
, wat hetzelfde is als RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. MetORDER BY
, dit stelt het frame in op alle rijen vanaf de partitiestart tot en met de laatste ORDER BY
van de huidige rij peer .
... wat de cumulatieve of lopende som is die u zoekt. Vetgedrukte nadruk van mij.
Rijen met dezelfde (circle_id, ea_year, ea_month)
zijn "peers" in deze vraag. Al deze tonen dezelfde lopende som met alle peers toegevoegd aan de som. Maar ik neem aan dat je tafel UNIQUE
is op (circle_id, ea_year, ea_month)
, dan is de sorteervolgorde deterministisch en heeft geen enkele rij peers.
Postgres 11 heeft tools toegevoegd om peers op te nemen/uit te sluiten met de nieuwe frame_exclusion
opties. Zie:
- Alle waarden samenvoegen die niet in dezelfde groep zitten
Nu, ORDER BY ... ea_month
werkt niet met tekenreeksen voor maandnamen . Postgres zou alfabetisch sorteren volgens de landinstelling.
Als je een echte date
hebt waarden die in uw tabel zijn opgeslagen, kunt u correct sorteren. Zo niet, dan stel ik voor om ea_year
te vervangen en ea_month
met een enkele kolom mon
van het type date
in uw tafel.
-
Transformeer wat je hebt met
to_date()
:to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Voor weergave kun je originele strings krijgen met
to_char()
:to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Hoewel het vastzit aan het ongelukkige ontwerp, zal dit werken:
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;