rows between 3 preceding and current row 表示当前行及前3行共4行参与滚动求和,需配合明确 order by 确保顺序,mysql 8.0+ 原生支持,sql server 2016+ 才完整支持该语法。

用 ROWS BETWEEN 3 PRECEDING AND CURRENT ROW 实现滚动求和
直接用 SUM() 窗口函数配合 ROWS BETWEEN 就能算出当前行及前3行的合计值,不需要自关联或子查询。关键不是“前3行”,而是“当前行 + 前3行”,共4行数据参与计算。
常见错误是写成 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW——这只会取前2行+当前行(共3行),漏掉第3行;或者漏写 ORDER BY,导致窗口顺序不可控,结果随机。
-
ORDER BY必须明确指定,且该列应具备唯一性或能稳定排序(比如时间戳、自增ID) - 如果分区字段存在,记得加
PARTITION BY,否则跨组数据会混算 - 开头几行(第1~3行)因前面不足3行,实际参与行数会自动缩减,这是预期行为,不是bug
MySQL 8.0+ 和 PostgreSQL 都支持,但 SQL Server 要注意版本
ROWS BETWEEN 语法在 MySQL 8.0、PostgreSQL 8.4+、Oracle 10g+ 中原生可用;SQL Server 从 2012 开始支持窗口函数,但 ROWS BETWEEN 要到 2016 才完整支持——2012/2014 中若写 ROWS BETWEEN 3 PRECEDING AND CURRENT ROW 会报错 Incorrect syntax near 'ROWS'。
SQL Server 2012–2014 用户得退而求其次,用 OFFSET + FETCH 模拟,或改用 LAG() 手动展开:
SELECT
val,
val + COALESCE(LAG(val, 1) OVER (ORDER BY id), 0) +
COALESCE(LAG(val, 2) OVER (ORDER BY id), 0) +
COALESCE(LAG(val, 3) OVER (ORDER BY id), 0) AS sum_last_4
FROM t;
这种写法可读性差、难维护,且一旦要改成“前10行”就得补9个 LAG,不推荐长期使用。
NULL 值会让 SUM() 结果变 NULL?不一定
SUM() 默认忽略 NULL,只对非空值求和。但如果整段窗口内所有值都是 NULL(比如前3行+当前行全为 NULL),SUM() 返回 NULL,不是 0。
是否需要转成 0,取决于业务逻辑:
- 想保持语义(无有效数据=未知),就不用处理
- 想统一输出数字,用
COALESCE(SUM(...), 0) - 注意:不要用
ISNULL(SUM(...), 0)在 PostgreSQL 里,它不存在;得用COALESCE
性能敏感时,避免在大表上反复 ORDER BY
窗口函数的 ORDER BY 会触发排序,如果底层表没有对应索引,可能产生临时文件或磁盘排序,尤其当按非主键字段(如 created_at)排序且数据量超百万时,延迟明显。
优化手段很实际:
- 确保
ORDER BY字段上有索引,例如CREATE INDEX idx_t_created ON t(created_at) - 如果只是按主键
id排序,且id是自增,多数引擎能利用聚集索引避免额外排序 - 别在
WHERE过滤前就开窗口——先WHERE再OVER,减少参与计算的行数
滚动求和看着简单,但排序依赖、NULL 处理、版本兼容这三点,任何一个没对齐,结果就 quietly 错了。











