sql >> Database >  >> RDS >> PostgreSQL

Cumulatieve som berekenen in PostgreSQL

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;



  1. Hoe de REGEX_REPLACE()-functie werkt in MySQL

  2. Optimalisatie van queryprestaties in MySQL

  3. Een inleiding tot de Java Security API

  4. Salesforce SOQL van Java