Zasadniczo potrzebujesz funkcji okna. To obecnie standardowa funkcja. Oprócz oryginalnych funkcji okien możesz używać dowolnych funkcja agregująca jako funkcja okna w Postgresie przez dodanie OVER
klauzula.
Szczególna trudność polega na tym, aby uzyskać właściwe partycje i porządek sortowania:
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;
I nie GROUP BY
.
Suma dla każdego wiersza jest obliczana od pierwszego wiersza w partycji do bieżącego wiersza - lub cytując instrukcję, aby być precyzyjnym:
Domyślną opcją kadrowania jest RANGE UNBOUNDED PRECEDING
, który jest taki sam jak RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. ZORDER BY
, ustawia to ramkę na wszystkie wiersze od partycjistart do ostatniego ORDER BY
bieżącego wiersza równorzędny .
... czyli suma skumulowana lub bieżąca, której szukasz. Pogrubiony nacisk na moje.
Wiersze z tym samym (circle_id, ea_year, ea_month)
są "rówieśnikami" w tym zapytaniu. Wszystkie z nich pokazują tę samą sumę bieżącą ze wszystkimi rówieśnikami dodanymi do sumy. Ale zakładam, że twoja tabela jest UNIQUE
w (circle_id, ea_year, ea_month)
, porządek sortowania jest deterministyczny i żaden wiersz nie ma równorzędnych pozycji.
Postgres 11 dodał narzędzia do włączania / wykluczania peerów z nowym frame_exclusion
opcje. Zobacz:
- Agregacja wszystkich wartości spoza tej samej grupy
Teraz ORDER BY ... ea_month
nie będzie działać z ciągami znaków dla nazw miesięcy . Postgres posortowałby alfabetycznie zgodnie z ustawieniami regionalnymi.
Jeśli masz aktualną date
wartości przechowywane w tabeli można poprawnie posortować. Jeśli nie, sugeruję zastąpienie ea_year
i ea_month
z pojedynczą kolumną mon
typu date
w twoim stole.
-
Przekształć to, co masz za pomocą
to_date()
:to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Do wyświetlania możesz uzyskać oryginalne ciągi za pomocą
to_char()
:to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Jeśli utknąłeś z niefortunnym projektem, to zadziała:
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;