在没有足够数据可用的情况下,使用带有前导空值的 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

online demo link

您可以使用另一个 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;

Demo on DB Fiddle:

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