在没有足够数据可用的情况下,使用带有前导空值的 SQL window 函数计算移动平均值
Calculate moving average using SQL window functions with leading null's where not enough data is avaliable
我想使用 SQL window 函数计算移动平均线。以下 2 "day" 移动平均值的示例基本上可以正常工作,但如果只有一个数据点可用,它也会计算平均值。只要没有足够的数据可用,我宁愿希望平均值为空
create table average(
nr int,
value float
);
insert into average values (1, 2), (2, 4), (3, 6), (3, 8), (4, 10);
SELECT
nr,
value,
AVG(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING)::FLOAT AS "Moving-Average-2"
FROM average;
结果:
1 2 2
2 4 3
3 6 5
3 8 7
4 10 9
预期结果:
1 2 null
2 4 3
3 6 5
3 8 7
4 10 9
编辑 1:
当然平均值可以是任何东西,不仅仅是 2.
我认为空值不会出现在 agv 的第一行,否则下面将使用
BETWEEN 1 PRECEDING AND CURRENT ROW
select nr, value,
avg(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS "Moving-Average-2"
from average;
在前一行和当前行之间
但你可以通过使用 case when
来处理它
select nr, value,
case when nr=1 then null else
avg(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) end AS "Moving-Average-2"
from average;
nr value Moving-Average-2
1 2
2 4 3
3 6 5
3 8 7
4 10 9
您可以使用另一个 window 函数 (COUNT()
) 来确保在进行计算之前 window 中至少有两条记录可用,例如:
SELECT
nr,
value,
CASE WHEN COUNT(*) OVER(ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING) > 1
THEN AVG(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING)::FLOAT
ELSE NULL
END AS "Moving-Average-2"
FROM average;
| nr | value | Moving-Average-2 |
| --- | ----- | ---------------- |
| 1 | 2 | |
| 2 | 4 | 3 |
| 3 | 6 | 5 |
| 3 | 8 | 7 |
| 4 | 10 | 9 |
由于您碰巧只在前一行和当前行之间形成平均值,因此使用 lag()
可能是最简单的:
select nr, value
,(value + lag(value, 1, NULL) OVER (ORDER BY nr)) / 2 AS "Moving-Average-2"
from average;
lag()
有一个重载变体,允许在没有行的情况下提供默认值(作为第三个参数)。提供 NULL
即可。或者,由于 NULL
是默认的默认值,所以:
... ,(value + lag(value) OVER (ORDER BY nr)) / 2 AS "Moving-Average-2"
虽然基础 table 列的类型为 float
,但在这种情况下您不需要转换为 float
。
这是假设列值已定义 NOT NULL
(如示例数据所示)。否则你还会得到 NULL
,其中前一行有 value IS NULL
而当前行有一个值,而 avg()
returns 在这种情况下的值! (或者这可能正是您想要的,考虑到您的问题。)
这可能是使用 window 规范的方便位置:
select a.*,
(case when row_number() over w > 1
then avg(value) over w
end) as running_average
from average a
window w as (order by nr rows between 1 preceding and current row);
我想使用 SQL window 函数计算移动平均线。以下 2 "day" 移动平均值的示例基本上可以正常工作,但如果只有一个数据点可用,它也会计算平均值。只要没有足够的数据可用,我宁愿希望平均值为空
create table average(
nr int,
value float
);
insert into average values (1, 2), (2, 4), (3, 6), (3, 8), (4, 10);
SELECT
nr,
value,
AVG(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING)::FLOAT AS "Moving-Average-2"
FROM average;
结果:
1 2 2
2 4 3
3 6 5
3 8 7
4 10 9
预期结果:
1 2 null
2 4 3
3 6 5
3 8 7
4 10 9
编辑 1: 当然平均值可以是任何东西,不仅仅是 2.
我认为空值不会出现在 agv 的第一行,否则下面将使用
BETWEEN 1 PRECEDING AND CURRENT ROW
select nr, value,
avg(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS "Moving-Average-2"
from average;
在前一行和当前行之间
但你可以通过使用 case when
来处理它select nr, value,
case when nr=1 then null else
avg(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) end AS "Moving-Average-2"
from average;
nr value Moving-Average-2
1 2
2 4 3
3 6 5
3 8 7
4 10 9
您可以使用另一个 window 函数 (COUNT()
) 来确保在进行计算之前 window 中至少有两条记录可用,例如:
SELECT
nr,
value,
CASE WHEN COUNT(*) OVER(ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING) > 1
THEN AVG(value) OVER (ORDER BY nr ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING)::FLOAT
ELSE NULL
END AS "Moving-Average-2"
FROM average;
| nr | value | Moving-Average-2 |
| --- | ----- | ---------------- |
| 1 | 2 | |
| 2 | 4 | 3 |
| 3 | 6 | 5 |
| 3 | 8 | 7 |
| 4 | 10 | 9 |
由于您碰巧只在前一行和当前行之间形成平均值,因此使用 lag()
可能是最简单的:
select nr, value
,(value + lag(value, 1, NULL) OVER (ORDER BY nr)) / 2 AS "Moving-Average-2"
from average;
lag()
有一个重载变体,允许在没有行的情况下提供默认值(作为第三个参数)。提供 NULL
即可。或者,由于 NULL
是默认的默认值,所以:
... ,(value + lag(value) OVER (ORDER BY nr)) / 2 AS "Moving-Average-2"
虽然基础 table 列的类型为 float
,但在这种情况下您不需要转换为 float
。
这是假设列值已定义 NOT NULL
(如示例数据所示)。否则你还会得到 NULL
,其中前一行有 value IS NULL
而当前行有一个值,而 avg()
returns 在这种情况下的值! (或者这可能正是您想要的,考虑到您的问题。)
这可能是使用 window 规范的方便位置:
select a.*,
(case when row_number() over w > 1
then avg(value) over w
end) as running_average
from average a
window w as (order by nr rows between 1 preceding and current row);