V zásadě potřebujete funkci okna. To je v dnešní době standardní funkce. Kromě originálních funkcí okna můžete použít jakékoli agregační funkci jako funkci okna v Postgresu připojením OVER
doložka.
Zvláštní obtíž je zde získat správné oddíly a pořadí řazení:
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;
A ne GROUP BY
.
Součet pro každý řádek se vypočítává od prvního řádku v oddílu po aktuální řádek – nebo přesněji cituji manuál:
Výchozí možnost orámování je
RANGE UNBOUNDED PRECEDING
, což je totéž jakoRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. PomocíORDER BY
, toto nastaví rámec tak, aby byly všechny řádky od začátku oddílu až po posledníORDER BY
aktuálního řádku vrstevníka .
... což je kumulativní nebo průběžná částka, o kterou vám jde. Tučné zdůraznění moje.
Řádky se stejným (circle_id, ea_year, ea_month)
jsou "peers" v tomto dotazu. Všechny vykazují stejný průběžný součet, přičemž k součtu se přidávají všichni kolegové. Ale předpokládám, že vaše tabulka je UNIQUE
dne (circle_id, ea_year, ea_month)
, pak je pořadí řazení deterministické a žádný řádek nemá rovnocenné.
Postgres 11 přidal nástroje pro zahrnutí / vyloučení kolegů pomocí nového frame_exclusion
možnosti. Viz:
- Agregace všech hodnot, které nejsou ve stejné skupině
Nyní ORDER BY ... ea_month
nebude fungovat s řetězci pro názvy měsíců . Postgres by seřadil abecedně podle nastavení národního prostředí.
Pokud máte aktuální date
hodnoty uložené ve vaší tabulce můžete správně třídit. Pokud ne, doporučuji nahradit ea_year
a ea_month
s jedním sloupcem mon
typu date
ve vaší tabulce.
-
Transformujte to, co máte, pomocí
to_date()
:to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Pro zobrazení můžete získat originální řetězce pomocí
to_char()
:to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
I když zůstanete u nešťastného designu, bude to fungovat:
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;