7

我试图在 SELECT 语句中根据同一 SELECT 语句中前 n 行中的列创建一个运行平均列。我需要的平均值基于结果集中的前 n 行。

让我解释

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

Average 列的前 3 行为空,因为没有先前的行。Average 列中的第 4 行显示前 3 行的 Number 列的平均值。

我需要一些帮助来尝试构建将执行此操作的 SQL Select 语句。

4

7 回答 7

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
于 2009-05-26T15:54:12.277 回答
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;
于 2009-05-26T16:51:12.240 回答
2

一个简单的自连接似乎比引用子查询的行好得多

生成 10k 行测试数据:

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 行的特殊情况从主查询中拉出来,如果你真的想要它在行集中,你可以 UNION ALL 回来。自加入查询:

;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 秒,Aaron Alton 演示的子查询方法大约需要 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 次。

于 2009-05-26T20:04:50.133 回答
1

编辑:我错过了应该平均前三个记录的点......

对于一般的运行平均值,我认为这样的事情会起作用:

SELECT
    id, number, 
    SUM(number) OVER (ORDER BY ID) / 
       ROW_NUMBER() OVER (ORDER BY ID) AS [RunningAverage]
FROM myTable
ORDER BY ID
于 2009-05-26T15:55:06.050 回答
0

在这里查看一些解决方案。我相信你可以很容易地适应其中之一。

于 2009-05-26T15:38:06.523 回答
0

如果您希望它真正具有高性能,并且不怕深入 SQL Server 很少使用的领域,您应该考虑编写自定义聚合函数。SQL Server 2005 和 2008 将 CLR 集成到表中,包括编写用户聚合函数的能力。到目前为止,自定义运行总聚合将是计算这样的运行平均值的最有效方法。

于 2009-05-26T16:06:27.277 回答
0

或者,您可以非规范化并存储预先计算的运行值。此处描述:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx

选择的性能尽可能快。当然,修改速度较慢。

于 2009-05-26T18:43:47.007 回答