必须用rows between显式定义帧边界,仅order by默认得累计和而非移动和;正确写法如rows between 2 preceding and current row(含当前行及前两行),缺between关键字会报错。

SQL窗口函数中ROWS BETWEEN的正确写法
移动总和(running sum over a sliding window)必须用 ROWS BETWEEN 显式定义帧边界,仅靠 ORDER BY + 默认 UNBOUNDED PRECEDING 得到的是累计和,不是移动和。
常见错误是写成这样:
SELECT amount, SUM(amount) OVER (ORDER BY date ROWS 2 PRECEDING)
这会报错——ROWS 2 PRECEDING 缺少 BETWEEN 关键字,语法不合法。正确形式必须是完整区间表达式。
-
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW:含当前行及前两行(共3行) -
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:前1行 + 当前行 + 后1行(共3行) -
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:标准累计和(非移动)
移动总和 vs 累计和:结果差异一目了然
假设按 date 排序的销售数据,每行 amount 为 [100, 200, 300, 400, 500]:
- 累计和(
SUM(amount) OVER (ORDER BY date)):[100, 300, 600, 1000, 1500] - 3行移动和(
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW):[100, 300, 600, 900, 1200]
关键区别在第4行:累计和包含全部前4行(100+200+300+400=1000),而移动和只取最近3行(200+300+400=900)。第1、2行因数据不足,自动按实际可用行计算(无补零)。
ORDER BY必须存在,且不能有NULL或重复值干扰排序稳定性
ROWS BETWEEN 依赖物理行序,而窗口函数的行序由 ORDER BY 决定。如果 ORDER BY 字段含 NULL,不同数据库处理方式不同(PostgreSQL 默认把 NULL 放最后,MySQL 可能放最前),会导致移动窗口滑动错位。
- 确保
ORDER BY字段非空,或显式加NULLS LAST(PostgreSQL/Oracle)或IS NOT NULL过滤 - 若排序字段可能重复(如多笔同日订单),需追加唯一列(如
id)保证排序确定性:ORDER BY date, id - SQLite 不支持
NULLS LAST,遇到 NULL 必须先清理或替换
性能与兼容性注意点
移动窗口计算是逐行扫描,数据量大时比普通聚合慢,但比自连接快得多。不过各数据库对 ROWS BETWEEN 的实现成熟度不同:
- PostgreSQL、SQL Server、Oracle、BigQuery:完全支持,行为一致
- MySQL 8.0+:支持,但
FOLLOWING起始位置不能是变量(只能是字面量) - SQLite 3.28+:支持,但不支持
UNBOUNDED FOLLOWING作为结束边界 - 旧版 MySQL(ROWS BETWEEN,只能用自连接或UDF模拟
真正容易被忽略的是边界截断逻辑——当窗口左边界超出分区开头时,数据库自动收缩窗口(不是报错也不是补零),这个行为在做同比/环比校验时可能引发隐性偏差。










