sql >> Databasteknik >  >> RDS >> PostgreSQL

Beräknar kumulativ summa i PostgreSQL

I grund och botten behöver du en fönsterfunktion. Det är en standardfunktion nuförtiden. Förutom äkta fönsterfunktioner kan du använda vilken som helst aggregerad funktion som fönsterfunktion i Postgres genom att lägga till en OVER klausul.

Den speciella svårigheten här är att få partitioner och sorteringsordning rätt:

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;

Och nej GROUP BY .

Summan för varje rad beräknas från den första raden i partitionen till den aktuella raden - eller citerar manualen för att vara exakt:

Standardinramningsalternativet är RANGE UNBOUNDED PRECEDING , vilket är samma som RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Med ORDER BY , detta ställer in ramen till att vara alla rader från partitionens start upp till den aktuella radens senaste ORDER BY peer .

... vilket är den ackumulerade eller löpande summan du är ute efter. Djärv betoning min.

Rader med samma (circle_id, ea_year, ea_month) är "kamrater" i den här frågan. Alla dessa visar samma löpande summa med alla peers adderade till summan. Men jag antar att din tabell är UNIQUE(circle_id, ea_year, ea_month) , då är sorteringsordningen deterministisk och ingen rad har jämlikar.

Postgres 11 har lagt till verktyg för att inkludera/exkludera peers med den nya frame_exclusion alternativ. Se:

  • Aggregering av alla värden som inte ingår i samma grupp

Nu, ORDER BY ... ea_month fungerar inte med strängar för månadsnamn . Postgres skulle sortera alfabetiskt enligt lokalinställningen.

Om du har ett verkligt date värden lagrade i din tabell kan du sortera ordentligt. Om inte, föreslår jag att du byter ut ea_year och ea_month med en enda kolumn mon av typen date i din tabell.

  • Förvandla det du har med to_date() :

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • För visning kan du hämta originalsträngar med to_char() :

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

Medan du har fastnat i den olyckliga designen kommer detta att fungera:

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. Exempel på en enkel sammanfogningssats i SQL Server

  2. Jag kan inte få mitt inloggningsformulär att ansluta interagera korrekt med mySQL-databasen

  3. SQL CASE-sats:Vad är det och vilka är de bästa sätten att använda den?

  4. Hur man använder SQL Server HierarchyID genom enkla exempel