sql >> Databasteknik >  >> RDS >> Mysql

Kumulativ summa över en uppsättning rader i mysql

UPPDATERA

MySQL 8.0 introducerar "fönsterfunktioner", funktionalitet motsvarande SQL Server "fönsterfunktioner" (med partitionering och ordning tillhandahållen av Transact-SQL OVER syntax), och Oracle "analytiska funktioner".

MySQL referensmanual 12.21 Fönsterfunktioner https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

Svaret som ges här är ett tillvägagångssätt för MySQL-versioner före 8.0.

ORIGINAL SVAR

MySQL tillhandahåller inte den typ av analytisk funktion du skulle använda för att få en löpande "kumulativ summa", som de analytiska funktionerna som är tillgängliga i andra DBMS (som Oracle eller SQL Server.)

Men det är möjligt att emulera vissa analytiska funktioner med MySQL.

Det finns (minst) två fungerande tillvägagångssätt:

En är att använda en korrelerad delfråga för att få delsumman. Detta tillvägagångssätt kan vara dyrt på stora uppsättningar och komplicerat om predikaten på den yttre frågan är komplicerade. Det beror verkligen på hur komplicerat det är att "flera kopplingar på flera bord" är. (Tyvärr stöder inte MySQL heller inte CTE.)

Det andra tillvägagångssättet är att använda MySQL-användarvariabler, för att göra en del kontrollpausbearbetning. "Knepet" här är att resultaten från din fråga sorteras (med hjälp av en ORDER BY) och sedan slå in din fråga i en annan fråga.

Jag ska ge ett exempel på det senare tillvägagångssättet.

På grund av ordningen som MySQL utför operationer, är cumulative_total kolumnen måste beräknas före värdet från id och day från den aktuella raden sparas i användarvariabler. Det är bara enklast att sätta den här kolumnen först.

Inline-vyn med alias som i (i frågan nedan) är bara till för att initiera användarvariablerna, ifall dessa redan är inställda i sessionen. Om dessa redan har värden tilldelade vill vi ignorera deras nuvarande värden, och det enklaste sättet att göra det är att initiera dem.

Din ursprungliga fråga lindas inom parentes och får ett alias, c i exemplet nedan. Den enda ändringen av din ursprungliga fråga är tillägget av en ORDER BY-sats, så att vi kan vara säkra på att vi bearbetar raderna från frågan i följd.

Den yttre markeringen kontrollerar om id och day värdet från den aktuella raden "matchar" föregående rad. Om de gör det lägger vi till amount från den aktuella raden till den kumulativa delsumman. Om de inte stämmer överens, återställer vi den kumulativa delsumman till noll och lägger till beloppet från den aktuella raden (eller, enklare, tilldela bara beloppet från den aktuella raden).

Efter att vi har gjort beräkningen av den kumulativa summan sparar vi id och day värden från den aktuella raden till användarvariabler, så att de är tillgängliga när vi bearbetar nästa rad.

Till exempel:

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

Om det är nödvändigt att returnera kolumnerna i en annan ordning, med kumulativ totalsumma som den sista kolumnen, är ett alternativ att linda in hela satsen i en uppsättning parens och använda den frågan som en inlinevy:

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d


  1. Hur Cot() fungerar i PostgreSQL

  2. Konsten att samla data i SQL från enkla till glidande aggregationer

  3. Ändra en inloggnings standardspråk i SQL Server

  4. Hitta en förälder på toppnivå i SQL