avg over 默认计算累积平均而非移动平均,因未显式指定窗口范围时默认为rows between unbounded preceding and current row;必须用rows between n preceding and current row定义滑动窗口,且order by字段需唯一稳定,避免range在重复值下导致窗口漂移。

AVG OVER 为什么算不出想要的移动平均?
直接写 AVG(column) OVER (ORDER BY time) 默认是累积平均,不是你想要的“最近 N 天”那种滑动窗口。窗口函数不指定范围时,ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 是默认行为,结果是“从第一条到当前行”的平均,不是移动平均。
- 必须显式用
ROWS BETWEEN N PRECEDING AND CURRENT ROW或RANGE BETWEEN ...定义窗口边界 -
RANGE按值(如日期)计算,但对重复时间戳可能意外扩大窗口;ROWS更可控,按物理行数滑动 - 如果排序字段有重复值(比如多条记录同一天),
RANGE可能包含更多行,导致平均值失真
怎么写一个 7 日移动平均(按日历天)?
假设表有 date 和 sales 字段,且每天最多一条记录。优先用 ROWS 避免歧义:
SELECT
date,
sales,
AVG(sales) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM sales_table;
注意:6 preceding + current = 7 行。如果数据存在缺失日期(比如某天没销售记录),这个写法会跳过空日,只取实际存在的最近 7 条记录——它按“行数”算,不是严格日历日。
- 要严格按日历日(含空日),得先用
GENERATE_SERIES(PostgreSQL)或递归 CTE 补全日期,再LEFT JOIN - MySQL 8.0+ 支持
ROWS,但不支持RANGE与日期类型直接配合(会报错),必须转为序号或使用变量模拟 - SQL Server 同样只认
ROWS,RANGE对DATE类型无效
ORDER BY 必须存在,且不能用表达式替代
OVER 子句里没有 ORDER BY,ROWS BETWEEN 就没意义——数据库无法确定“前几行”是谁。常见错误包括:
- 漏写
ORDER BY,结果看似正常但逻辑错乱(实际是未定义行为) - 用
ORDER BY id代替时间字段,而id不反映业务顺序(比如异步插入导致乱序) - 在
ORDER BY中用函数如ORDER BY DATE(date),某些数据库(如旧版 Hive)不支持,需提前生成排序列
性能和 NULL 值怎么处理?
AVG() 自动忽略 NULL 值,但窗口长度仍按 ROWS 计数。例如窗口设为 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW,若这 3 行中有 2 个 NULL,AVG 只对剩下那个非空值求平均,结果就是那个值本身——不是 NULL,这点容易误判。
- 想让结果在数据不足时返回
NULL,得加条件:CASE WHEN COUNT(sales) OVER (...) - 大数据量下,
ORDER BY字段务必建索引,否则AVG OVER可能触发全局排序,性能断崖下跌 - Spark SQL 和 Flink SQL 的窗口语法基本兼容,但 Hive 3.1- 对
ROWS支持有限,部分版本只认UNBOUNDED开头的写法
移动平均真正麻烦的从来不是语法,而是对“窗口对齐方式”的预期管理:你是要固定行数、固定日历跨度,还是按业务事件滚动?选错 ROWS 还是 RANGE,或者忽略排序字段的语义一致性,结果就 quietly 错了。











