2009-05-26 34 views
7

Tôi đang cố gắng có một cột trung bình đang chạy trong câu lệnh SELECT dựa trên một cột từ n hàng trước đó trong cùng một câu lệnh SELECT. Mức trung bình tôi cần dựa trên n hàng trước trong resultset.Câu lệnh SQL Chọn để tính cột trung bình đang hoạt động

Hãy để tôi giải thích

Id  Number  Average 
1    1   NULL 
2    3   NULL 
3    2   NULL 
4    4    2 <----- Average of (1, 3, 2),Numbers from previous 3 rows 
5    6    3 <----- Average of (3, 2, 4),Numbers from previous 3 rows 
.    .    . 
.    .    . 

3 hàng đầu tiên của cột trung bình là null bởi vì không có hàng trước. Hàng 4 trong cột Trung bình cho biết mức trung bình của cột Số từ 3 hàng trước đó.

Tôi cần một số trợ giúp cố gắng xây dựng câu lệnh SQL Select sẽ thực hiện việc này.

+0

Bạn đang sử dụng loại cơ sở dữ liệu SQL nào? –

+0

Tôi đang sử dụng SQL Server 2008. – HYP

+0

Im nghĩ đây là một trong những trường hợp hiếm hoi mà con trỏ sẽ nhanh nhất ... chỉ cần giữ 3 hàng cuối cùng trong vars ... –

Trả lời

11

này nên làm điều đó:

--Test Data 
CREATE TABLE RowsToAverage 
    (
    ID int NOT NULL, 
    Number int NOT NULL 
    ) 

INSERT RowsToAverage(ID, Number) 
SELECT 1, 1 
UNION ALL 
SELECT 2, 3 
UNION ALL 
SELECT 3, 2 
UNION ALL 
SELECT 4, 4 
UNION ALL 
SELECT 5, 6 
UNION ALL 
SELECT 6, 8 
UNION ALL 
SELECT 7, 10 

--The query 
;WITH NumberedRows 
AS 
(
SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
FROM RowsToAverage rta 
) 

SELECT nr.ID, nr.Number, 
     CASE 
      WHEN nr.RowNumber <=3 THEN NULL 
      ELSE ( SELECT avg(Number) 
        FROM NumberedRows 
        WHERE RowNumber < nr.RowNumber 
        AND  RowNumber >= nr.RowNumber - 3 
       ) 
     END AS MovingAverage 
FROM NumberedRows nr 
+0

+1, nó chạy đúng trên hệ thống của tôi –

+0

+ 1- Giải pháp rất thanh lịch. MVP, MVP, MVP! –

+0

Giải pháp rất thanh lịch. Với 9.000 hàng, nó mất khoảng 45 giây trên máy chủ dev của tôi. Có cách nào để sử dụng kỹ thuật này hiệu quả hơn không. –

0

Kiểm tra một số giải pháp here. Tôi chắc chắn rằng bạn có thể thích nghi một trong số họ một cách dễ dàng đủ.

+1

Trong khi điều này về mặt lý thuyết có thể trả lời câu hỏi, [nó sẽ là thích hợp hơn] (http://meta.stackexchange.com/q/8259) để bao gồm các các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. – Sklivvz

1

Edit: Tôi bị mất điểm rằng nó nên trung bình ba kỷ lục trước đó ...

Đối với một trung bình chạy chung, tôi nghĩ rằng một cái gì đó như thế này sẽ làm việc :

SELECT 
    id, number, 
    SUM(number) OVER (ORDER BY ID)/
     ROW_NUMBER() OVER (ORDER BY ID) AS [RunningAverage] 
FROM myTable 
ORDER BY ID 
+0

khi sử dụng bảng RowsToAverage của @Aaron Alton (tôi đã thay đổi FROM MyTable thành FROM RowsToAverage), tôi gặp lỗi: Msg 102, Level 15, State 1, Line 3 Cú pháp không đúng gần 'order'. –

+0

Bạn đang sử dụng RDBMS nào? Các chức năng cửa sổ chỉ có sẵn trong SQL 2005 và cao hơn. –

+0

Tôi nên thêm rằng OP đề cập họ đang sử dụng SQL 2008. –

0

Nếu bạn muốn điều này thực sự thực hiện và không ngại đào sâu vào một khu vực hiếm khi được sử dụng SQL Server, bạn nên xem xét viết hàm tổng hợp tùy chỉnh. SQL Server 2005 và 2008 đã đưa CLR vào bảng, bao gồm khả năng viết các hàm tổng hợp của người dùng. Tổng số tổng hợp chạy tùy chỉnh sẽ là cách hiệu quả nhất để tính trung bình chạy như thế này, cho đến nay.

8

Giả sử rằng các cột Id là tuần tự, đây là một câu hỏi đơn giản cho một bảng có tên là "MyTable":

SELECT 
    b.Id, 
    b.Number, 
    (
     SELECT 
     AVG(a.Number) 
     FROM 
     MyTable a 
    WHERE 
     a.id >= (b.Id - 3) 
     AND a.id < b.Id 
     AND b.Id > 3 
    ) as Average 
FROM 
    MyTable b; 
+0

Uhmm. Giả sử các chuỗi tuần tự, điều này cũng hoạt động. +1 –

+0

Điều này cũng có thể hoạt động nếu không có hàng nào bị xóa trong bảng. Tôi chấp nhận giải pháp của Aaron Alton vì row_number() OVER (ORDER BY rta.ID ASC) hoạt động cho mọi trường hợp. – HYP

2

Một tự đơn giản tham gia sẽ có vẻ để thực hiện tốt hơn nhiều so với một hàng tham khảo subquery

Tạo 10k dòng dữ liệu thử nghiệm:

drop table test10k 
create table test10k (Id int, Number int, constraint test10k_cpk primary key clustered (id)) 

;WITH digits AS (
    SELECT 0 as Number 
    UNION SELECT 1 
    UNION SELECT 2 
    UNION SELECT 3 
    UNION SELECT 4 
    UNION SELECT 5 
    UNION SELECT 6 
    UNION SELECT 7 
    UNION SELECT 8 
    UNION SELECT 9 
) 
,numbers as (
    SELECT 
     (thousands.Number * 1000) 
     + (hundreds.Number * 100) 
     + (tens.Number * 10) 
     + ones.Number AS Number 
    FROM digits AS ones 
    CROSS JOIN digits AS tens 
    CROSS JOIN digits AS hundreds 
    CROSS JOIN digits AS thousands 
) 
insert test10k (Id, Number) 
select Number, Number 
from numbers 

tôi sẽ kéo trường hợp đặc biệt của 3 hàng đầu tiên trong số các truy vấn chính, bạn có thể UNION TẤT CẢ những người trở lại trong nếu bạn thực sự muốn nó trong tập hợp hàng. Tự tham gia truy vấn:

;WITH NumberedRows 
AS 
(
    SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
    FROM test10k rta 
) 

SELECT nr.ID, nr.Number, 
    avg(trailing.Number) as MovingAverage 
FROM NumberedRows nr 
    join NumberedRows as trailing on trailing.RowNumber between nr.RowNumber-3 and nr.RowNumber-1 
where nr.Number > 3 
group by nr.id, nr.Number 

Trên máy tính của tôi này mất khoảng 10 giây, cách tiếp cận subquery đó Aaron Alton chứng minh mất khoảng 45 giây (sau khi tôi thay đổi nó để phản ánh kiểm tra bảng nguồn của tôi):

;WITH NumberedRows 
AS 
(
    SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
    FROM test10k rta 
) 
SELECT nr.ID, nr.Number, 
    CASE 
      WHEN nr.RowNumber <=3 THEN NULL 
      ELSE ( SELECT avg(Number) 
          FROM NumberedRows 
          WHERE RowNumber < nr.RowNumber 
          AND    RowNumber >= nr.RowNumber - 3 
        ) 
    END AS MovingAverage 
FROM NumberedRows nr 

Nếu bạn làm một SET STATISTICS PROFILE ON, bạn có thể thấy tự join có 10k thực hiện trên spool bảng. Truy vấn con có 10k thực thi trên bộ lọc, tổng hợp và các bước khác.

Các vấn đề liên quan