AVG() OVER()默认计算累积平均而非滑动平均,因未指定窗口帧时默认ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;需显式定义如ROWS BETWEEN 2 PRECEDING AND CURRENT ROW才能实现最近3行滑动均值。

SQL里AVG()加OVER()为什么算出来不是想要的滑动平均?
因为没指定窗口帧(frame),默认是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是从组开头累加到当前行——这不是滑动,是累积平均。要平滑过渡,必须显式定义窗口范围。
- 想算最近3条记录的均值:用
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW - 想包含当前行+前后各1行(共3行中心对称):用
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING - 如果排序字段有重复值,
ORDER BY列必须带唯一性保障(比如加id),否则PRECEDING/FOLLOWING行为不可控
MySQL 8.0+ 和 PostgreSQL 的AVG() OVER()写法差异在哪?
核心逻辑一致,但MySQL对NULL值更敏感:只要窗口内任意value为NULL,整行结果就是NULL;PostgreSQL会自动跳过NULL再计算均值。
- MySQL中稳妥做法:先用
COALESCE(value, 0)或WHERE value IS NOT NULL预处理 - PostgreSQL可直接写
AVG(value) OVER(...),但要注意AVG()本身返回numeric类型,和原字段类型可能不一致 - 两者都不支持
RANGE帧在非数字/日期列上使用(比如按字符串排序时不能写RANGE BETWEEN ...)
用AVG() OVER()做滑动平均时,分组边界容易出什么错?
最常见的是PARTITION BY和ORDER BY配合不当,导致窗口跨组“泄露”。比如按user_id分组、按created_at排序,但某用户数据时间戳为空,就会被挤到组首或组尾,把别组数据拉进当前窗口。
- 务必检查
ORDER BY字段是否在PARTITION BY内完全有序(无空值、无歧义) - 避免用
ORDER BY status这种低基数列做滑动依据——窗口可能包含几十行相同status,实际滑动距离失控 - 测试时用
COUNT(*) OVER(...)并列输出,确认每行窗口实际包含几行数据
性能陷阱:滑动窗口AVG在大数据量下为什么突然变慢?
窗口函数本身不索引友好,尤其是ROWS BETWEEN N PRECEDING AND M FOLLOWING这类双向窗口,数据库必须为每一行重新扫描局部数据段。当分组内单次查询超10万行,响应延迟会指数上升。
- 优先考虑用物化中间表:先按
PARTITION BY + ORDER BY排好序存成临时表,再加自增序号模拟行号,用关联代替窗口 - PostgreSQL可用
pg_stat_progress_window_agg视图监控执行进度 - MySQL 8.0.22+ 支持
WINDOW命名复用,避免重复写复杂OVER()子句,但不减少计算量
滑动窗口的“平滑”依赖于严格的时间/顺序连续性,而现实数据总有空缺、乱序、重复。别指望AVG() OVER()自动补全或插值——它只负责按你写的帧切片计算,其余得自己兜底。










