Durchschnitt der letzten 4 Läufe eines Produkts


7

Ich muss das durchschnittliche Gewicht eines Produkts in den letzten 4 Produktionsläufen anzeigen. Ich bin mir nicht sicher, wie ich es am besten beschreiben soll, außer einem Beispiel: Stellen wir uns vor, ich habe die folgende Tabelle, in der ein Produkt nach dem Erstellungsdatum und dem durchschnittlichen Gewicht des Produkts für diesen Tag aufgeführt ist:

+---------+---------+--------+
| Product |  Date   | Weight |
+---------+---------+--------+
|  900000 | Jan 1   | 20.0   |
|  900000 | March 3 | 12.2   |
|  900000 | July 6  | 15.0   |
|  900000 | July 7  | 14.0   |
|  900000 | Aug 6   | 3.0    |
|  800000 | June 2  | 14.0   |
|  800000 | June 3  | 12.0   |
+---------+---------+--------+

Das Endergebnis, das ich suche, ist das Hinzufügen einer Spalte, die das durchschnittliche Gewicht für die letzten 4 Daten enthält, für die das Produkt ausgeführt wurde.

+---------+---------+--------+----------------+
| Product |  Date   | Weight | Average Weight |
+---------+---------+--------+----------------+
|  900000 | Jan 1   | 20.0   | NULL           |
|  900000 | March 3 | 12.2   | NULL           |
|  900000 | July 6  | 15.0   | NULL           |
|  900000 | July 7  | 14.0   | NULL           |
|  900000 | Aug 6   | 3.0    | 15.3           | Jan1+Mar3+July6+July7/4
|  900000 | Aug 8   | 13.0   | 11.05          | Mar3+July6+July7+Aug6/4
|  800000 | June 2  | 14.0   | NULL           |
|  800000 | June 3  | 12.0   | NULL           |
|  800000 | June 4  | 12.0   | NULL           |
|  800000 | June 5  | 12.0   | NULL           |
|  800000 | June 6  | 12.0   | 12.5           | etc...
+---------+---------+--------+----------------+

Die NULL-Werte sind nur vorhanden, da Sie in diesem Beispiel den Durchschnitt der letzten 4 Läufe nicht berechnen können, da die Daten nicht vorhanden sind.

Könnte mich jemand in die Richtung weisen, in die ich suchen muss, um so etwas zu tun?

Antworten:


10

Beispieldaten:

CREATE TABLE dbo.Thing
(
    Product integer NOT NULL,
    TheDate date NOT NULL,
    TheWeight decimal(5, 1) NOT NULL
);

INSERT dbo.Thing
    (Product, TheDate, TheWeight)
VALUES
    (900000, CONVERT(date, '20160101', 112), 20.0),
    (900000, '20160303', 12.2),
    (900000, '20160706', 15.0),
    (900000, '20160707', 14.0),
    (900000, '20160806', 3.0 ),
    (900000, '20160808', 13.0 ),
    (800000, '20160602', 14.0),
    (800000, '20160603', 12.0),
    (800000, '20160604', 12.0),
    (800000, '20160605', 12.0),
    (800000, '20160606', 12.0);

Lösung:

Die allgemeine Idee hierbei ist, die erweiterten Fensteraggregatfunktionen zu verwenden, die in SQL Server 2012 und höher verfügbar sind.

Die einzige Falte ist, dass AVGüber ein Fenster keine Null zurückgegeben wird, wenn es kleiner als die erforderlichen vier Zeilen ist. Um dies zu beheben, berechnen wir auch die Anzahl der im Fenster gefundenen Zeilen mit COUNT. Ein einfacher CASEAusdruck kann dann verwendet werden, um eine Null zurückzugeben, wenn das Fenster weniger als vier Zeilen enthält:

SELECT
    T.Product,
    T.TheDate,
    T.TheWeight,
    [Average Weight] =
        CASE
            WHEN
                4 > COUNT_BIG(*) OVER (
                    PARTITION BY T.Product
                    ORDER BY T.Product, T.TheDate
                    ROWS BETWEEN 4 PRECEDING
                    AND 1 PRECEDING
                    )
                THEN NULL
            ELSE
                AVG(T.TheWeight) OVER (
                    PARTITION BY T.Product
                    ORDER BY T.Product, T.TheDate
                    ROWS BETWEEN 4 PRECEDING
                    AND 1 PRECEDING
                    )
        END
FROM dbo.Thing AS T
ORDER BY
    T.Product,
    T.TheDate;

Führen Sie die Abfrage im Stack Exchange Data Explorer aus

Ausgabe:

Ergebnisse

Ausführungsplan

Mehr Informationen:

Fensterfunktionen in SQL Server

Verwandte Frage:

Rollende Summe des Datumsbereichs mithilfe von Fensterfunktionen


5

Dies ist ein gleitender Durchschnitt, bei dem es sich um eine Fensterfunktion in SQL Server 2012 und höher handelt. Sie könnten es so lösen:

SELECT Product, [Date], Weight,
       (CASE WHEN _runningCount>=4
             THEN _runningTotal/4.0
             END) AS [Average weight]
FROM (
    SELECT Product, [Date], Weight,
           SUM(Weight) OVER (
               PARTITION BY Product
               ORDER BY [Date]
               ROWS BETWEEN 4 PRECEDING AND 1 PRECEDING) AS _runningTotal,
           SUM(1) OVER (
               PARTITION BY Product
               ORDER BY [Date]
               ROWS BETWEEN 4 PRECEDING AND 1 PRECEDING) AS _runningCount
    FROM theTable
) AS sub;

Hier sind die wichtigsten Punkte:

  • Die OVER ()Klausel beschreibt, dass die Aggregation (in diesem Fall SUM) in einem Fenster stattfindet. In unserem Fall möchten wir die letzten vier Zeilen für das aktuelle Produkt (die Partition) nach Datum sortieren.
  • SUM(1) fungiert als Zählung.
  • Zur besseren Lesbarkeit habe ich die beiden Fensterfunktionen in eine Unterabfrage eingefügt sub.
  • Wenn _runningCountdann 4 oder mehr ist, können wir die laufende Summe der letzten vier Zeilen durch 4 teilen, andernfalls geben Sie zurück NULL.

Wenn Sie die laufende Summe zu einem bestimmten Zeitpunkt (einschließlich der ersten Daten) zurückgeben möchten, ändern Sie die CASEin Folgendes:

SELECT ...
       _runningTotal/_runningCount AS [Average weight]

Vielen Dank! Vielen Dank, dass Sie auch die Komponenten erklärt haben. Erst kürzlich sind wir nach
einiger
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.