PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Kumulative Summe in PostgreSQL berechnen

Grundsätzlich benötigen Sie eine Fensterfunktion. Das ist heutzutage eine Standardfunktion. Zusätzlich zu echten Fensterfunktionen können Sie beliebige verwenden Aggregatfunktion als Fensterfunktion in Postgres durch Anhängen eines OVER Klausel.

Die besondere Schwierigkeit besteht hier darin, Partitionen und Sortierreihenfolge richtig hinzubekommen:

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;

Und nein GROUP BY .

Die Summe für jede Zeile wird von der ersten Zeile in der Partition bis zur aktuellen Zeile berechnet - oder um genau zu sein, um das Handbuch zu zitieren:

Die standardmäßige Framing-Option ist RANGE UNBOUNDED PRECEDING , was dasselbe ist wie RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Mit ORDER BY , setzt dies den Rahmen auf alle Zeilen vom Partitionsstart bis zur letzten Zeile ORDER BY der aktuellen Zeile Peer .

... was die kumulierte oder laufende Summe ist, nach der Sie suchen. Fettdruck von mir.

Zeilen mit demselben (circle_id, ea_year, ea_month) sind "Peers" in dieser Abfrage. Alle zeigen die gleiche laufende Summe, wobei alle Peers zur Summe hinzugefügt werden. Aber ich nehme an, Ihre Tabelle ist UNIQUE am (circle_id, ea_year, ea_month) , dann ist die Sortierreihenfolge deterministisch und keine Zeile hat Peers.

Postgres 11 hat Tools hinzugefügt, um Peers mit dem neuen frame_exclusion einzuschließen/auszuschließen Optionen. Siehe:

  • Aggregieren aller Werte, die nicht in derselben Gruppe sind

Jetzt ORDER BY ... ea_month funktioniert nicht mit Zeichenfolgen für Monatsnamen . Postgres sortiert alphabetisch nach der Locale-Einstellung.

Wenn Sie das aktuelle date haben Werte, die in Ihrer Tabelle gespeichert sind, können Sie richtig sortieren. Wenn nicht, schlage ich vor, ea_year zu ersetzen und ea_month mit einer einzigen Spalte mon vom Typ date in Ihrer Tabelle.

  • Wandeln Sie das, was Sie haben, mit to_date() um :

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Zur Anzeige können Sie Original-Strings mit to_char() erhalten :

      to_char(mon, 'Month') AS ea_month
      to_char(mon, 'YYYY') AS ea_year
    

Während Sie bei dem unglücklichen Design bleiben, wird dies funktionieren:

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;