sql >> Database teknologi >  >> RDS >> PostgreSQL

Beregning af kumulativ sum i PostgreSQL

Som udgangspunkt har du brug for en vinduesfunktion. Det er en standardfunktion i dag. Ud over ægte vinduesfunktioner kan du bruge hvilken som helst aggregeret funktion som vinduesfunktion i Postgres ved at tilføje en OVER klausul.

Den særlige vanskelighed her er at få partitioner og sorteringsrækkefølge rigtigt:

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;

Og nej GROUP BY .

Summen for hver række beregnes fra den første række i partitionen til den nuværende række - eller citerer manualen for at være præcis:

Standardindstillingsindstillingen er RANGE UNBOUNDED PRECEDING , hvilket er det samme som RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Med ORDER BY , dette indstiller rammen til at være alle rækker fra partitionen starter op til den aktuelle rækkes sidste ORDER BY peer .

... som er den kumulative eller løbende sum, du går efter. Fed fremhævelse mine.

Rækker med den samme (circle_id, ea_year, ea_month) er "peers" i denne forespørgsel. Alle disse viser den samme løbende sum med alle peers tilføjet summen. Men jeg antager, at din tabel er UNIQUE(circle_id, ea_year, ea_month) , så er sorteringsrækkefølgen deterministisk, og ingen række har peers.

Postgres 11 tilføjede værktøjer til at inkludere/ekskludere peers med den nye frame_exclusion muligheder. Se:

  • Aggregering af alle værdier, der ikke er i samme gruppe

Nu, ORDER BY ... ea_month fungerer ikke med strenge for månedsnavne . Postgres ville sortere alfabetisk i henhold til lokalitetsindstillingen.

Hvis du har en faktisk date værdier gemt i din tabel kan du sortere korrekt. Hvis ikke, foreslår jeg at erstatte ea_year og ea_month med en enkelt kolonne mon af typen date i din tabel.

  • Transformer det du har med to_date() :

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Til visning kan du få originale strenge med to_char() :

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

Mens du sidder fast med det uheldige design, vil dette virke:

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. Automatisk dataindsamling:Databasefiler og logiske drev i MS SQL Server

  2. Indsættelse af SQL Server-data i Salesforce med en markør

  3. Automatisk generering af svarfil

  4. Sådan får du Oracle til at oprette tabelsætning i SQL*Plus