2009-05-26 23 views
7

我想在SELECT語句中的一個運行平均列基於來自同一個SELECT語句中前n行的一列。我需要的平均值是基於結果集中前n行的。SQL選擇語句來計算運行平均列

讓我解釋一下

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行平均柱是空的。 「平均值」列中的第4行顯示前3行中「數字」列的平均值。

我需要一些幫助,試圖構建一個SQL Select語句來執行此操作。

+0

你在使用什麼樣的SQL數據庫? – 2009-05-26 15:33:31

+0

我正在使用SQL Server 2008. – HYP 2009-05-26 15:35:11

+0

我認爲這是真正罕見的情況之一,遊標將會變得最快......只是保持最後3行在變量中... – 2009-05-26 15:36:58

回答

11

這應做到:

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

查看一些解決方案here。我相信你可以很容易地適應其中之一。

1

編輯:我錯過了它應該平均3次前科的點...

對於一般的移動平均,我認爲像這樣的工作:

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

如果你想這是真正的高性能,並arn't怕挖到SQL Server的一個很少使用的區域,你應該考慮編寫自定義的聚合函數。 SQL Server 2005和2008帶來了CLR集成到表中,包括編寫用戶聚合函數的能力。到目前爲止,自定義運行總計將是計算此類運行平均值的最有效方式。

8

假設ID列順序,這裏有一個名爲「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; 
2

簡單的自我加入似乎表現得比行更好的參考子查詢

生成10,000行測試數據:

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 

我會拉第3行的特殊情況下進行的主要的查詢,你可以聯合所有那些回來,如果你真的想要在行集。自連接查詢:

;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 

在我的機器上這需要大約10秒,子查詢的方法,阿龍奧爾頓證明需要約45秒(之後我改變了它,以反映我的測試源表):

;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 

如果您執行SET STATISTICS PROFILE ON,您可以看到自加入在桌面假脫機上執行10k。子查詢有10k在過濾器,聚合和其他步驟上執行。