如何针对boolean=1的行计算前3天boolean=0的fact滚动平均值?
解决指定条件下的滚动平均值计算问题
原数据表
| date | id1 | id2 | fact | boolean |
|---|---|---|---|---|
| 2022-01-01 | i1 | l1 | 100 | 0 |
| 2022-01-02 | i1 | l1 | 10 | 1 |
| 2022-01-03 | i1 | l1 | 110 | 0 |
| 2022-01-04 | i1 | l1 | 70 | 0 |
| 2022-01-05 | i1 | l1 | 20 | 1 |
| 2022-01-05 | i1 | l1 | 30 | 1 |
| 2022-01-06 | i1 | l1 | 40 | 0 |
| 2022-01-07 | i1 | l1 | 50 | 0 |
| 2022-01-08 | i1 | l1 | 70 | 0 |
| 2022-01-09 | i1 | l1 | 100 | 1 |
| 2022-01-09 | i1 | l1 | 10 | 1 |
| 2022-01-10 | i1 | l1 | 200 | 0 |
需求说明
仅在boolean列值为1的行中,计算其前3天内boolean列值为0的fact列的滚动平均值,其余行的rolling_avg字段为空。
预期结果
| date | id1 | id2 | fact | boolean | rolling_avg |
|---|---|---|---|---|---|
| 2022-01-01 | i1 | l1 | 100 | 0 | |
| 2022-01-02 | i1 | l1 | 10 | 1 | 100 |
| 2022-01-03 | i1 | l1 | 110 | 0 | |
| 2022-01-04 | i1 | l1 | 70 | 0 | |
| 2022-01-05 | i1 | l1 | 20 | 1 | 90 |
| 2022-01-05 | i1 | l1 | 30 | 1 | 90 |
| 2022-01-06 | i1 | l1 | 40 | 0 | |
| 2022-01-07 | i1 | l1 | 50 | 0 | |
| 2022-01-08 | i1 | l1 | 70 | 0 | |
| 2022-01-09 | i1 | l1 | 100 | 1 | 53.33 |
| 2022-01-09 | i1 | l1 | 10 | 1 | 53.33 |
| 2022-01-10 | i1 | l1 | 200 | 0 |
示例说明
- 2022-01-05的
boolean=1行,前3天为2022-01-02至2022-01-04,其中boolean=0的行fact值为110和70,平均值为90; - 2022-01-09的
boolean=1行,前3天为2022-01-06至2022-01-08,所有行boolean=0,fact值为40、50、70,平均值为53.33。
尝试的错误代码
用户最初尝试的窗口函数未满足需求:
AVG(expression) OVER (PARTITION BY id1, id2 ORDER BY date DESC ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) as rolling_avg
正确实现方法
逻辑分析
原代码存在三个核心问题:
- 用
ROWS BETWEEN按行计数,无法准确匹配“前3天”的日期范围要求; - 未过滤
boolean=0的行来计算平均值; - 未控制仅
boolean=1的行显示结果,其余行置空。
正确思路:
- 使用范围窗口结合日期函数限定前3天的区间;
- 在平均值计算中通过条件判断仅纳入
boolean=0的fact值; - 最后通过条件语句确保只有
boolean=1的行展示结果。
具体SQL代码
SELECT date, id1, id2, fact, boolean, CASE WHEN boolean = 1 THEN ROUND( AVG(CASE WHEN boolean = 0 THEN fact ELSE NULL END) OVER ( PARTITION BY id1, id2 ORDER BY date RANGE BETWEEN INTERVAL '3 days' PRECEDING AND INTERVAL '1 day' PRECEDING ), 2 ) ELSE NULL END AS rolling_avg FROM your_table_name;
代码解释
RANGE BETWEEN INTERVAL '3 days' PRECEDING AND INTERVAL '1 day' PRECEDING:限定窗口为当前行日期的前3天到前1天,排除当天数据;AVG(CASE WHEN boolean = 0 THEN fact ELSE NULL END):仅对窗口内boolean=0的fact值计算平均值;CASE WHEN boolean = 1 THEN ... ELSE NULL:仅在boolean=1的行展示计算结果,其余行置空;ROUND(..., 2):将平均值保留两位小数,匹配预期结果格式。
注意:不同SQL方言对日期区间的语法略有差异,比如MySQL中需替换为
RANGE BETWEEN DATE_SUB(date, INTERVAL 3 DAY) AND DATE_SUB(date, INTERVAL 1 DAY),上述代码适用于PostgreSQL等支持INTERVAL语法的数据库。
内容的提问来源于stack exchange,提问作者Mahesh Shankar
相关产品推荐
相关产品推荐

