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;