用于计算运行平均值列的 SQL Select 语句
-
06-09-2019 - |
题
我试图在 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 行为空,因为没有先前的行。平均值列中的第 4 行显示前 3 行的数字列的平均值。
我需要一些帮助来尝试构建执行此操作的 SQL Select 语句。
解决方案
此应该这样做:
--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
其他提示
假设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;
一个简单的自我加入似乎表现比一个行子查询引用
好得多生成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秒钟,子查询的方法,阿龙奥尔顿证明需要大约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
如果你做一组统计PROFILE ON,您可以看到自加入具有10K执行在桌子上滑。子查询具有10K在过滤器上,聚合执行,并且其它步骤。
编辑:我错过了这一点,它应该平均3个以前的记录...
对于一般的移动平均,我觉得像这样的工作:
SELECT
id, number,
SUM(number) OVER (ORDER BY ID) /
ROW_NUMBER() OVER (ORDER BY ID) AS [RunningAverage]
FROM myTable
ORDER BY ID
这里检查出一些解决方案。我敢肯定,你能适应其中的一个很轻松了。
如果您希望它真正具有高性能,并且不害怕深入研究 SQL Server 中很少使用的区域,那么您应该考虑编写自定义聚合函数。SQL Server 2005 和 2008 将 CLR 集成引入表中,包括编写用户聚合函数的能力。到目前为止,自定义运行总聚合将是计算此类运行平均值的最有效方法。
或者,您可以反规范化并存储预先计算的运行值。此处描述:
选择的性能是最快的。当然,修改速度会比较慢。
不隶属于 StackOverflow