sql >> Databáze >  >> RDS >> PostgreSQL

Výpočet kumulativního součtu v PostgreSQL

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éž jako RANGE 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;



  1. Architektura pro bezpečnost:Průvodce pro MySQL

  2. C#:Objekt nelze přetypovat z DbNull na jiné typy

  3. SELECT dotaz s podmínkou CASE a SUM()

  4. jak přeskočit špatný řádek ve zdroji plochého souboru ssis