Erstellen Sie eine kumulative Summenspalte in MySQL


72

Ich habe einen Tisch, der so aussieht:

id   count
1    100
2    50
3    10

Ich möchte eine neue Spalte mit dem Namen cumulative_sum hinzufügen, damit die Tabelle folgendermaßen aussieht:

id   count  cumulative_sum
1    100    100
2    50     150
3    10     160

Gibt es eine MySQL-Update-Anweisung, die dies problemlos ermöglicht? Was ist der beste Weg, um dies zu erreichen?

Antworten:


88

Wenn die Leistung ein Problem darstellt, können Sie eine MySQL-Variable verwenden:

set @csum := 0;
update YourTable
set cumulative_sum = (@csum := @csum + count)
order by id;

Alternativ können Sie die cumulative_sumSpalte entfernen und bei jeder Abfrage berechnen:

set @csum := 0;
select id, count, (@csum := @csum + count) as cumulative_sum
from YourTable
order by id;

Dies berechnet die laufende Summe auf laufende Weise :)


7
Verwenden Sie einen Cross-Join, um die Variable zu definieren, ohne sie verwenden zu müssen SET.
OMG Ponys

Mein Tisch hat 36 Millionen Datensätze, das war also sehr hilfreich, um die Dinge zu beschleunigen!
Kirk Ouimet

Beachten Sie, dass die Bestellung nach cumulative_sum möglicherweise einen vollständigen Tabellenscan erzwingt.
Matt

1
Das funktioniert und scheint ziemlich schnell zu sein; Irgendwelche Vorschläge, wie dies erweitert werden kann, um eine kumulative Summe in einer Gruppe zu machen? zB gruppieren nach Nameoder ähnlich, und dann eine kumulative Summe nur für Datensätze mit dem gleichen Namen machen
zaitsman

@zaitsman Sie können es als Unterabfrage verwenden. Gruppieren Sie in der äußeren Abfrage nach Belieben und verwenden Sie dann die MAX () MySQL-Funktion, um die richtige kumulative Zusammenfassung (die letzte Zusammenfassung) zu erhalten, die für die Datensätze innerhalb der Gruppe berechnet wurde.
Pascal

104

Verwenden einer korrelierten Abfrage:


  SELECT t.id,
         t.count,
         (SELECT SUM(x.count)
            FROM TABLE x
           WHERE x.id <= t.id) AS cumulative_sum
    FROM TABLE t
ORDER BY t.id

Verwenden von MySQL-Variablen:


  SELECT t.id,
         t.count,
         @running_total := @running_total + t.count AS cumulative_sum
    FROM TABLE t
    JOIN (SELECT @running_total := 0) r
ORDER BY t.id

Hinweis:

  • Das JOIN (SELECT @running_total := 0) rist ein Cross-Join und ermöglicht die Deklaration von Variablen, ohne dass ein separater SETBefehl erforderlich ist .
  • Der Tabellenalias rwird von MySQL für jede Unterabfrage / abgeleitete Tabelle / Inline-Ansicht benötigt

Vorsichtsmaßnahmen:

  • MySQL-spezifisch; nicht auf andere Datenbanken portierbar
  • Das ORDER BYist wichtig; Es stellt sicher, dass die Reihenfolge mit dem OP übereinstimmt und größere Auswirkungen auf die kompliziertere Verwendung von Variablen haben kann (IE: psuedo ROW_NUMBER / RANK-Funktionalität, die MySQL fehlt).

Ich würde "ORDER BY t.id ASC" zur Hauptabfrage hinzufügen, nur um sicherzustellen, dass es immer funktioniert
Wacek

Mein erster Gedanke war auch, ORDER BY hinzuzufügen. Aber das ist egal. Bis die Addition zumindest nicht assoziativ wird :)
Dercsár

@OMG Poines: Ich denke, Sie müssen SELECTim JOIN (SELECT @running_total := 0)Teil des Variablenbeispiels ein verwenden.
Daniel Vassallo

1
für "Verwenden einer korrelierten Abfrage" Woher kommt Ihre Tabelle x?
allan.simon

Sofern keine interne Optimierung stattfindet, entspricht die korrelierte Unterabfrage einer dreieckigen Verknüpfung, die in O (N ^ 2) -Zeit ausgeführt wird - was nicht skaliert.
Marc L.

18

MySQL 8.0 / MariaDB unterstützt Fenster SUM(col) OVER():

SELECT *, SUM(cnt) OVER(ORDER BY id) AS cumulative_sum
FROM tab;

Ausgabe:

┌─────┬──────┬────────────────┐
│ id  │ cnt  │ cumulative_sum │
├─────┼──────┼────────────────┤
│  1  │ 100  │            100 │
│  2  │  50  │            150 │
│  3  │  10  │            160 │
└─────┴──────┴────────────────┘

db <> Geige


1
Ich suche kumulative Summe mit Windows-Funktion. Danke.
DatabaseCoder

3
UPDATE t
SET cumulative_sum = (
 SELECT SUM(x.count)
 FROM t x
 WHERE x.id <= t.id
)

3
Obwohl das OP um ein Update gebeten hat, ist dies denormalisiert und wird wahrscheinlich unpraktisch sein, um es korrekt zu warten.
Matthew Flaschen

3
select Id, Count, @total := @total + Count as cumulative_sum
from YourTable, (Select @total := 0) as total ;

4
Bitte erklären Sie Ihre Antwort
Rohit Gupta

Die Antwort funktioniert und ist ein Liner. Außerdem wird die Variable zu Beginn der Auswahl auf Null initialisiert / zurückgesetzt.
Rosercostin

2

Beispielabfrage

SET @runtot:=0;
SELECT
   q1.d,
   q1.c,
   (@runtot := @runtot + q1.c) AS rt
FROM
   (SELECT
       DAYOFYEAR(date) AS d,
       COUNT(*) AS c
    FROM  orders
    WHERE  hasPaid > 0
    GROUP  BY d
    ORDER  BY d) AS q1

1

Sie können auch einen Trigger erstellen, der die Summe vor jeder Einfügung berechnet

delimiter |

CREATE TRIGGER calCumluativeSum  BEFORE INSERT ON someTable
  FOR EACH ROW BEGIN

  SET cumulative_sum = (
     SELECT SUM(x.count)
        FROM someTable x
        WHERE x.id <= NEW.id
    )

    set  NEW.cumulative_sum = cumulative_sum;
  END;
|

Ich habe das nicht getestet


1

Wählen Sie id, count, sum (count) over (Reihenfolge nach Anzahl absteigend) als kumulative_summe aus tableName aus.

Ich habe die Summenaggregatfunktion für die Zählspalte verwendet und dann die over-Klausel verwendet. Es fasst jede der Zeilen einzeln zusammen. Die erste Reihe wird nur 100 sein. Die zweite Reihe wird 100 + 50 sein. Die dritte Reihe ist 100 + 50 + 10 und so weiter. Im Grunde genommen ist jede Zeile die Summe aller und aller vorherigen Zeilen und die allerletzte ist die Summe aller Zeilen. Die Art und Weise, dies zu betrachten, ist, dass jede Zeile die Summe des Betrags ist, bei dem die ID kleiner oder gleich sich selbst ist.


2
Während dies das Problem lösen könnte, ist es besser, es ein wenig zu erklären, damit es anderen zugute kommt :)
Tiw

Dies ist keine Co-bezogene Unterabfrage oder eine Unterabfrage für diese Angelegenheit ... Co-bezogene Unterabfrage folgt, SELECT ...., (SELECT .... FROM table2 WHERE table2.id = table1.id ) FROM table1 was Sie haben, ist eine Fensterabfrage.
Raymond Nijland

0
  select t1.id, t1.count, SUM(t2.count) cumulative_sum
    from table t1 
        join table t2 on t1.id >= t2.id
    group by t1.id, t1.count

Schritt für Schritt:

1- Angesichts der folgenden Tabelle:

select *
from table t1 
order by t1.id;

id  | count
 1  |  11
 2  |  12   
 3  |  13

2 - Informationen nach Gruppen abrufen

select *
from table t1 
    join table t2 on t1.id >= t2.id
order by t1.id, t2.id;

id  | count | id | count
 1  | 11    | 1  |  11

 2  | 12    | 1  |  11
 2  | 12    | 2  |  12

 3  | 13    | 1  |  11
 3  | 13    | 2  |  12
 3  | 13    | 3  |  13

3- Schritt 3: Summieren Sie alle Zählungen nach t1.id-Gruppe

select t1.id, t1.count, SUM(t2.count) cumulative_sum
from table t1 
    join table t2 on t1.id >= t2.id
group by t1.id, t1.count;


id  | count | cumulative_sum
 1  |  11   |    11
 2  |  12   |    23
 3  |  13   |    36

Schritt für Schritt hinzugefügt, um die endgültige Abfrage zu verstehen
Flavio_cava
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.