Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Kumulative Summe über eine Reihe von Zeilen in mysql

AKTUALISIEREN

MySQL 8.0 führt „Fensterfunktionen“ ein, eine Funktionalität, die den „Fensterfunktionen“ von SQL Server entspricht (mit Partitionierung und Reihenfolge, die von Transact-SQL OVER bereitgestellt wird). Syntax) und Oracle "analytische Funktionen".

MySQL-Referenzhandbuch 12.21 Fensterfunktionen https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

Die hier gegebene Antwort ist ein Ansatz für MySQL-Versionen vor 8.0.

ORIGINALE ANTWORT

MySQL bietet nicht die Typanalysefunktion, die Sie verwenden würden, um eine laufende "kumulative Summe" zu erhalten, wie die Analysefunktionen, die in anderen DBMS (wie Oracle oder SQL Server) verfügbar sind.

Es ist jedoch möglich, einige analytische Funktionen mit MySQL zu emulieren.

Es gibt (mindestens) zwei praktikable Ansätze:

Eine besteht darin, eine korrelierte Unterabfrage zu verwenden, um die Zwischensumme zu erhalten. Dieser Ansatz kann bei großen Mengen teuer und kompliziert sein, wenn die Prädikate für die äußere Abfrage kompliziert sind. Es hängt wirklich davon ab, wie kompliziert "mehrere Verknüpfungen in mehreren Tabellen" sind. (Leider unterstützt MySQL auch keine CTEs.)

Der andere Ansatz besteht darin, MySQL-Benutzervariablen zu verwenden, um eine Steuerungsunterbrechungsverarbeitung durchzuführen. Der "Trick" besteht hier darin, die Ergebnisse Ihrer Abfrage zu sortieren (mithilfe von ORDER BY) und Ihre Abfrage dann in eine andere Abfrage einzupacken.

Ich gebe ein Beispiel für den letzteren Ansatz.

Aufgrund der Reihenfolge, in der MySQL Operationen ausführt, wird die cumulative_total Spalte muss vor dem Wert von id berechnet werden und day aus der aktuellen Zeile werden in Benutzervariablen gespeichert. Es ist einfach am einfachsten, diese Spalte an die erste Stelle zu setzen.

Die Inline-Ansicht mit dem Alias ​​i (in der Abfrage unten) ist nur dazu da, die Benutzervariablen zu initialisieren, nur für den Fall, dass diese bereits in der Sitzung festgelegt sind. Wenn diesen bereits Werte zugewiesen sind, möchten wir ihre aktuellen Werte ignorieren, und der einfachste Weg, dies zu tun, besteht darin, sie zu initialisieren.

Ihre ursprüngliche Abfrage wird in Klammern eingeschlossen und erhält einen Alias, c im Beispiel unten. Die einzige Änderung an Ihrer ursprünglichen Abfrage ist das Hinzufügen einer ORDER BY-Klausel, sodass wir sicher sein können, dass wir die Zeilen aus der Abfrage der Reihe nach verarbeiten.

Die äußere Auswahl prüft, ob die id und day Wert aus der aktuellen Zeile „übereinstimmen“ mit der vorherigen Zeile. Wenn ja, fügen wir den amount hinzu von der aktuellen Zeile zur kumulativen Zwischensumme. Wenn sie nicht übereinstimmen, setzen wir die kumulierte Zwischensumme auf null zurück und addieren den Betrag aus der aktuellen Zeile (oder weisen einfacher den Betrag aus der aktuellen Zeile zu).

Nachdem wir die Berechnung der kumulativen Summe durchgeführt haben, speichern wir die id und day Werte aus der aktuellen Zeile in Benutzervariablen, sodass sie verfügbar sind, wenn wir die nächste Zeile verarbeiten.

Zum Beispiel:

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

Wenn es notwendig ist, die Spalten in einer anderen Reihenfolge zurückzugeben, mit der kumulativen Summe als letzte Spalte, besteht eine Möglichkeit darin, die gesamte Anweisung in einen Satz von Klammern einzuschließen und diese Abfrage als Inline-Ansicht zu verwenden:

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