UNBOUNDED PRECEDING 指当前分区第一行,UNBOUNDED FOLLOWING 指当前分区最后一行;二者必须配合 ORDER BY 和显式 ROWS 才能正确生效,否则默认帧行为易致结果错误。

UNBOUNDED PRECEDING 和 UNBOUNDED FOLLOWING 到底指哪?
在 SUM() OVER 中写 UNBOUNDED,不是“随便累计”,而是明确指向窗口的边界位置。其中 UNBOUNDED PRECEDING 表示从分区第一行开始累加,UNBOUNDED FOLLOWING 表示累加到分区最后一行结束。若想“累计全部数据”,通常只需用 UNBOUNDED PRECEDING —— 因为累计是向前看的,不需要往后延伸。
不加 ORDER BY 时 SUM OVER(UNBOUNDED PRECEDING) 会怎样?
这是最容易踩的坑:如果 OVER 子句里没写 ORDER BY,SQL 标准规定窗口帧(frame)默认是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,但此时“当前行”没有逻辑顺序,数据库可能把整张表当作一个无序集合,导致结果不可预测(PostgreSQL 报错,SQL Server 可能返回全表总和,MySQL 8.0+ 会拒绝执行)。所以必须显式写 ORDER BY。
- 正确写法:
SUM(amount) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 省略
ROWS关键字也可能触发RANGE模式,在有重复排序值时产生意外聚合(比如多个相同date导致多行共享同一累计值) - 保险起见,始终显式声明
ROWS
如何真正实现“累计全部数据”(即每行都显示全量 SUM)?
这不是累计,是广播式全表聚合。这时不该用 UNBOUNDED PRECEDING,而应去掉 ORDER BY 并显式指定全范围帧:
SUM(amount) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
但注意:这要求窗口不能带 PARTITION BY,否则只是每个分区内全量;如果确实要全表一个总和,更清晰的做法其实是直接用子查询或 CROSS JOIN,避免误解窗口语义。
- 仅当需要每行都显示全表
SUM且不改变原行数时,才用UNBOUNDED FOLLOWING - 若已有
PARTITION BY category,那UNBOUNDED FOLLOWING累计的是该 category 内全部,不是全表 - MySQL 8.0+、PostgreSQL、SQL Server 都支持该语法;SQLite 目前不支持
UNBOUNDED FOLLOWING在窗口帧中
ORDER BY 字段重复时累计值“跳变”的原因
用 RANGE 默认模式(或显式写了 RANGE)时,相同 ORDER BY 值的所有行会获得相同的累计结果,因为 RANGE 把它们视作同一逻辑点。例如按 sale_date 排序,三天销量都是 2024-05-01,则这三行的 SUM() OVER(...) 值完全一致,等于前三行之和——而不是逐行递增。
- 解决方法:改用
ROWS模式,或在ORDER BY后追加唯一字段(如ORDER BY sale_date, id) - 检查执行计划或
EXPLAIN输出,确认实际生效的是ROWS还是RANGE - 某些数据库(如旧版 MySQL)不支持
ROWS显式声明,只能靠补充排序字段规避
窗口函数的“累计”行为高度依赖 ORDER BY 和帧定义,UNBOUNDED 不是魔法前缀,它只回答“从哪来、到哪去”。漏掉 ORDER BY 或混淆 ROWS/RANGE,比写错函数名更容易让结果静默出错。










