avg() over() 实现滚动平均需显式定义窗口范围,默认为unbounded preceding to current row;必须用order by确保顺序稳定,推荐rows between指定固定长度滑动窗口,并注意null处理与性能优化。

AVG() OVER() 的基本写法和窗口定义
滚动平均值本质是“在某个动态数据范围内求平均”,AVG() 配合 OVER() 是最直接的实现方式,但必须显式定义窗口范围,否则默认是 UNBOUNDED PRECEDING TO CURRENT ROW(从第一行到当前行),这往往不是你想要的“固定长度滚动”。
关键点在于用 ROWS BETWEEN 明确指定滑动窗口大小。比如计算最近3条记录(含当前行)的平均值:
SELECT
date,
sales,
AVG(sales) OVER (
ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS rolling_avg_3
FROM sales_data;
注意:2 PRECEDING 表示往前取2行,加上 CURRENT ROW 正好3行。如果写成 1 PRECEDING AND 1 FOLLOWING,就是以当前行为中心的3行——结果不同,取决于业务语义。
ORDER BY 是强制要求,且影响结果稳定性
OVER() 中的 ORDER BY 不仅决定滚动方向,还决定了“哪几行算‘最近’”。没有它会报错(SQL Server、PostgreSQL、MySQL 8.0+ 均如此)。
常见陷阱:
- 用非唯一字段(如多个同一天的销售记录)排序,会导致相同
date的行顺序不确定,滚动结果每次执行可能不同 - 没加
ORDER BY就直接套用,数据库直接拒绝执行,错误信息类似Window frame specified for function 'AVG' requires an ordering clause
稳妥做法:确保 ORDER BY 字段组合能唯一确定每一行位置,例如 ORDER BY date, id。
处理 NULL 和边界行的默认行为
AVG() 本身会自动忽略 NULL 值,但滚动窗口中若前几行全是 NULL,结果仍是 NULL;而首尾几行因窗口不完整,也会出现“有效行数不足”的情况。
例如第1行只有自己,第2行最多2行,AVG() 只对实际存在的非空值计算,不会补零或插值。如果你需要“不足3行时返回 NULL”还是“强行按现有行平均”,得提前确认业务规则。
若需填充默认值(如用 0 代替 NULL),不能直接在 AVG() 外套 COALESCE(),因为 AVG(NULL) 是 NULL,但 AVG(1, NULL) = 1.0 —— 它只跳过 NULL,不改变分母。真要控制分母,得用 COUNT() + SUM() 手动算。
性能敏感场景下慎用大范围窗口
滚动平均本身是 O(n) 窗口函数,但当 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 或超大固定范围(如 1000 PRECEDING)遇上千万级表,排序+逐行扫描开销明显。
优化建议:
- 确保
ORDER BY字段有索引(如(date, id)) - 避免在子查询或视图里嵌套多层
AVG() OVER()—— 每一层都重新触发窗口计算 - 如果只是月度/季度滚动均值,优先考虑按时间分组聚合再自连接,比全量窗口更快
真正难的不是写对语法,而是想清楚“滚动”到底按什么粒度对齐、边界怎么处理、NULL 算不算进分母——这些逻辑一旦定错,后面所有分析都偏了。











