Fondamentalmente, hai bisogno di una funzione finestra. Questa è una caratteristica standard al giorno d'oggi. Oltre alle funzioni della finestra originali, puoi utilizzare qualsiasi funzione di aggregazione come funzione finestra in Postgres aggiungendo un OVER
clausola.
La difficoltà speciale qui è ottenere partizioni e ordinare correttamente:
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;
E no GROUP BY
.
La somma per ogni riga viene calcolata dalla prima riga nella partizione alla riga corrente, o citando il manuale per la precisione:
L'opzione di inquadratura predefinita è RANGE UNBOUNDED PRECEDING
, che è lo stesso di RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Con ORDER BY
, questo imposta il frame in modo che sia tutte le righe dalla partizione dall'inizio fino all'ultimo ORDER BY
della riga corrente pari .
... che è la somma cumulativa o parziale che stai cercando. Enfasi in grassetto la mia.
Righe con lo stesso (circle_id, ea_year, ea_month)
sono "coetanei" in questa domanda. Tutti quelli mostrano la stessa somma parziale con tutti i peer aggiunti alla somma. Ma presumo che la tua tabella sia UNIQUE
su (circle_id, ea_year, ea_month)
, quindi l'ordinamento è deterministico e nessuna riga ha peer.
Postgres 11 ha aggiunto strumenti per includere/escludere peer con il nuovo frame_exclusion
opzioni. Vedi:
- Aggregazione di tutti i valori non nello stesso gruppo
Ora, ORDER BY ... ea_month
non funzionerà con le stringhe per i nomi dei mesi . Postgres ordina in ordine alfabetico in base alle impostazioni locali.
Se hai una date
effettiva i valori memorizzati nella tabella possono essere ordinati correttamente. In caso contrario, suggerisco di sostituire ea_year
e ea_month
con una singola colonna mon
di tipo date
nella tua tabella.
-
Trasforma ciò che hai con
to_date()
:to_date(ea_year || ea_month , 'YYYYMonth') AS mon
-
Per la visualizzazione, puoi ottenere stringhe originali con
to_char()
:to_char(mon, 'Month') AS ea_month to_char(mon, 'YYYY') AS ea_year
Anche se bloccato con lo sfortunato design, funzionerà:
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;