UNBOUNDED PRECEDING 表示窗口帧的逻辑起点,即当前分区中按 ORDER BY 排序后的首行,而非全表物理首行;它必须与 ORDER BY 和显式 ROWS BETWEEN 搭配使用才生效,缺一不可。

UNBOUNDED PRECEDING 是什么,它真能“从起点开始”?
UNBOUNDED PRECEDING 不是指整个表的物理第一行,而是指当前窗口框架(frame)中逻辑上的“最远可及前边界”。它的起点取决于 ORDER BY 子句和分区(PARTITION BY)——如果没写 PARTITION BY,那才是全表;写了,就只在每个分区内从该分区的首行算起。
常见误解是以为加了 UNBOUNDED PRECEDING 就自动覆盖所有历史数据,结果发现窗口没“累积”起来,其实是漏写了 ORDER BY。没有 ORDER BY,SQL 标准规定窗口帧默认是 ROWS BETWEEN CURRENT ROW AND CURRENT ROW,此时 UNBOUNDED PRECEDING 根本不生效。
- 必须搭配
ORDER BY才能激活帧定义 - 若用了
PARTITION BY order_date,那UNBOUNDED PRECEDING的“起点”就是该order_date分区内的第一行(按ORDER BY排序后) - PostgreSQL、Snowflake、BigQuery、Spark SQL 都支持;MySQL 8.0+ 支持,但 MySQL 5.7 不支持窗口函数
怎么写一个真正从分区首行累加的 SUM 窗口?
典型场景:按用户分组,按时间排序,计算每个订单的累计金额。关键不是只写 UNBOUNDED PRECEDING,而是整套帧子句要完整:
SELECT
user_id,
order_time,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY order_time
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumsum
FROM orders;
注意三点:
-
ROWS BETWEEN ...必须显式写出,不能省略(有些方言如 BigQuery 允许省略ROWS,但语义可能降级为RANGE,导致重复值合并) -
ORDER BY order_time决定了“首行”是谁;如果order_time有重复,建议加二级排序(如ORDER BY order_time, order_id)避免非确定性 - 用
ROWS而不是RANGE,否则相同order_time的多行会被捆在一起计算,不一定符合“逐行累加”预期
为什么有时候 UNBOUNDED PRECEDING 像没起作用?
最常遇到的静默失败:查询跑通、没报错,但 cumsum 列每行都等于当前 amount,看起来像没累加。大概率是以下之一:
- 漏写
ORDER BY—— 这是最硬性的前提,缺了就退化为单行窗口 - 误写成
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,且ORDER BY列存在重复值(例如多个订单同秒下单),RANGE会把它们全纳入当前帧 - 数据库方言差异:SQL Server 默认用
RANGE,而 PostgreSQL 默认用ROWS;不显式声明ROWS可能导致行为不一致 -
PARTITION BY分得太细(比如按 minute 级时间分区),导致每个分区只有一两行,自然“起点”就近了
UNBOUNDED PRECEDING 和 MAX() / FIRST_VALUE() 搭配要注意什么?
它不只是给 SUM() 用的。但不同聚合函数对帧边界的敏感度不同:
-
FIRST_VALUE(col) OVER (ORDER BY t ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)确实总返回当前分区中t最小那行的col值 -
MAX(col) OVER (...)在UNBOUNDED PRECEDING下等价于“到当前行为止的最大值”,但若用RANGE且ORDER BY列重复,可能提前触发更新 - 慎用
LAST_VALUE(col)配合UNBOUNDED PRECEDING:默认帧是CURRENT ROW,必须显式写成ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING才能取分区末尾值
真正容易被忽略的是:帧定义一旦写死,就和聚合函数无关——UNBOUNDED PRECEDING 描述的是行集合,不是计算逻辑。你换函数,集合不变;但集合是否合理,得靠 PARTITION BY 和 ORDER BY 共同决定。











