I grund och botten behöver du en fönsterfunktion. Det är en standardfunktion nuförtiden. Förutom äkta fönsterfunktioner kan du använda vilken som helst aggregerad funktion som fönsterfunktion i Postgres genom att lägga till en OVER klausul.
Den speciella svårigheten här är att få partitioner och sorteringsordning rätt:
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;
Och nej GROUP BY .
Summan för varje rad beräknas från den första raden i partitionen till den aktuella raden - eller citerar manualen för att vara exakt:
Standardinramningsalternativet är
RANGE UNBOUNDED PRECEDING, vilket är samma somRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. MedORDER BY, detta ställer in ramen till att vara alla rader från partitionens start upp till den aktuella radens senasteORDER BYpeer .
... vilket är den ackumulerade eller löpande summan du är ute efter. Djärv betoning min.
Rader med samma (circle_id, ea_year, ea_month) är "kamrater" i den här frågan. Alla dessa visar samma löpande summa med alla peers adderade till summan. Men jag antar att din tabell är UNIQUE på (circle_id, ea_year, ea_month) , då är sorteringsordningen deterministisk och ingen rad har jämlikar.
Postgres 11 har lagt till verktyg för att inkludera/exkludera peers med den nya frame_exclusion alternativ. Se:
- Aggregering av alla värden som inte ingår i samma grupp
Nu, ORDER BY ... ea_month fungerar inte med strängar för månadsnamn . Postgres skulle sortera alfabetiskt enligt lokalinställningen.
Om du har ett verkligt date värden lagrade i din tabell kan du sortera ordentligt. Om inte, föreslår jag att du byter ut ea_year och ea_month med en enda kolumn mon av typen date i din tabell.
-
Förvandla det du har med
to_date():to_date(ea_year || ea_month , 'YYYYMonth') AS mon -
För visning kan du hämta originalsträngar med
to_char():to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Medan du har fastnat i den olyckliga designen kommer detta att fungera:
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;