Trung bình 4 lần chạy gần nhất của một sản phẩm


7

Tôi cần hiển thị trọng lượng trung bình của một sản phẩm trong 4 lần sản xuất gần đây nhất. Tôi không chắc cách mô tả nó tốt nhất ngoài một ví dụ: Hãy tưởng tượng tôi có bảng sau liệt kê một sản phẩm theo ngày sản phẩm được tạo ra và trọng lượng trung bình của sản phẩm cho ngày đó:

+---------+---------+--------+
| 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   |
+---------+---------+--------+

Kết quả cuối cùng tôi đang tìm kiếm là thêm một cột bao gồm Trọng lượng trung bình cho 4 ngày cuối cùng mà sản phẩm được chạy, vì vậy đại loại như thế này:

+---------+---------+--------+----------------+
| 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...
+---------+---------+--------+----------------+

Các NULL chỉ ở đó vì trong mẫu này, bạn không thể tính trung bình trong 4 lần chạy gần nhất vì dữ liệu không có ở đó.

Bất cứ ai có thể chỉ cho tôi theo hướng tôi cần phải làm để làm một cái gì đó như thế này?

Câu trả lời:


10

Dữ liệu mẫu:

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);

Giải pháp:

Ý tưởng chung ở đây là sử dụng các hàm tổng hợp cửa sổ mở rộng có sẵn trong SQL Server 2012 trở lên.

Nếp nhăn duy nhất là AVGkhông trả về null trên một cửa sổ nếu nó nhỏ hơn bốn hàng bắt buộc. Để giải quyết điều đó, chúng tôi cũng tính toán số lượng hàng được tìm thấy trong cửa sổ bằng cách sử dụng COUNT. Một CASEbiểu thức đơn giản sau đó có thể được sử dụng để trả về null nếu cửa sổ chứa ít hơn bốn hàng:

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;

Chạy truy vấn trên Stack Exchange Data Explorer

Đầu ra:

Các kết quả

Kế hoạch thực hiện

Thêm thông tin:

Các chức năng của Window trong SQL Server

Câu hỏi liên quan:

Tổng số phạm vi ngày sử dụng các chức năng cửa sổ


5

Đây là một mức trung bình, là một chức năng được cửa sổ trong SQL Server 2012 và mới hơn. Bạn có thể giải quyết nó như thế này:

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;

Đây là điểm mấu chốt:

  • Các OVER ()điều khoản mô tả rằng tập hợp (SUM trong trường hợp này) diễn ra trong một cửa sổ. Trong trường hợp của chúng tôi, chúng tôi muốn tổng hợp bốn hàng gần đây nhất, cho sản phẩm hiện tại (phân vùng), được sắp xếp theo ngày.
  • SUM(1) hoạt động như một tính.
  • Để dễ đọc, tôi đã đặt hai hàm cửa sổ trong một truy vấn con , sub.
  • Sau đó, nếu _runningCountlà 4 hoặc nhiều hơn, chúng ta có thể chia tổng số chạy của bốn hàng gần đây nhất cho 4, nếu không, trả về NULL.

Nếu bạn muốn trả lại tổng số hoạt động tại bất kỳ thời điểm nào (kể cả vài ngày đầu tiên), bạn sẽ thay đổi CASEthành một cái gì đó như:

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

Cảm ơn bạn! Cảm ơn vì đã giải thích các thành phần, vừa mới quay trở lại DB sau một lúc, rất nhiều thứ đã thay đổi :)
Neostim 3/03/2016
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.