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 BY
peer .
... 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;