必须显式写ORDER BY + ROWS BETWEEN,缺一不可;漏ORDER BY会报错或结果漂移,漏ROWS BETWEEN则默认为RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(累积平均),而非滑动窗口。

必须显式写 ORDER BY + ROWS BETWEEN,缺一不可;漏掉 ORDER BY 会报错或结果漂移,漏掉 ROWS BETWEEN 默认变成累积平均,不是你要的滑动窗口。
为什么只写 AVG(col) OVER (ORDER BY time) 不行
这个写法在多数数据库里会退化成 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是“从头算到当前行”。如果有多条记录共享同一个 time(比如日粒度聚合后同一天多笔),它们会被打包进同一窗口,导致某天均值突然跳变。MySQL 8.0+ 直接报错:Window definition requires ORDER BY;PostgreSQL 可能跑通但结果不可复现。
- 错误示例:
AVG(sales) OVER (ORDER BY order_date)→ 实际是累积平均,不是滑动 - 正确前提:先明确你要几行窗口,再反推
ROWS BETWEEN参数 - 排序字段必须能唯一确定物理行序,否则同一 SQL 多次执行结果可能不同
ROWS BETWEEN 的数字怎么数才不翻车
ROWS BETWEEN N PRECEDING AND CURRENT ROW 表示“当前行 + 往上数 N 行”,总共 N+1 行。很多人误以为 2 PRECEDING 就是 2 行,其实它是 3 行窗口(含当前)。
- 3 日移动平均(含当天)→
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW - 5 日中心窗口(前2、当前、后2)→
ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING - 相邻三行(前1、当前、后1)→
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING - 首尾几行天然不满额:第 1 行窗口只有 1 行,这是标准行为,不是 bug
排序字段重复或缺失时怎么办
仅靠 ORDER BY order_date 非常危险——日期重复、缺失、NULL 都会导致窗口错位。真实业务中必须兜底唯一性。
- 推荐写法:
ORDER BY order_date, order_id(order_id是主键或唯一标识) - 时间精度不够?用
ORDER BY order_time, created_at, id多级保底 - 日期有 NULL?加
NULLS LAST或先过滤:WHERE order_date IS NOT NULL - 要按“最近 7 天”而非“最近 7 行”?得先补全日期(如
GENERATE_SERIES或左连接日历表),再开窗
分区(PARTITION BY)和性能陷阱
没加 PARTITION BY 就直接全表滑动,轻则结果错乱(比如用户 A 的订单混进用户 B 的窗口),重则查询卡死。尤其大表不分区时,ROWS BETWEEN 的计算成本随数据量非线性上升。
- 按用户算 7 日均值?必须:
PARTITION BY user_id ORDER BY order_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW - 窗口越大、分区越粗,内存和 CPU 消耗越高;PostgreSQL/MySQL 8.0+ 优化较好,SQLite 或旧版 SQL Server 可能全表扫描
- 避免
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW用于大表——它等于每行都重算前面所有行 -
AVG()自动忽略NULL值,但ROWS仍占槽位;若窗口内大量NULL,实际参与计算的非空行可能远少于预期
最易被忽略的是排序字段的稳定性——哪怕只是少加一个 order_id,线上就可能跑出完全不同的移动平均值,而且难以复现。










