unbounded preceding表示当前分区第一行,须与between搭配使用;rows下严格按物理行序,range下按排序值扩展;默认frame因order by有无而异,误用易致计算错误。

Frame子句决定“当前行能看到哪些行”
窗口函数不是对整张表算一次,而是为每一行动态划定一个计算范围——这个范围就是由FRAME子句控制的。不写FRAME时,数据库会按规则自动补默认值,但这个默认值在不同场景下完全不同:
- 有
ORDER BY时,默认是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - 没
ORDER BY时,默认是GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING(等价于整个分区)
实际中漏掉或写错FRAME,常导致累计和变成全分区总和、移动平均只算一行,甚至结果与预期完全相反。
UNBOUNDED PRECEDING不是“从第0行开始”
UNBOUNDED PRECEDING是SQL标准里的特殊标记,表示“从当前分区的第一行无条件起始”,不能被替换成0 PRECEDING——后者在PostgreSQL报frame starting offset must not be negative,SQL Server直接拒绝解析。
-
ROWS BETWEEN 0 PRECEDING AND CURRENT ROW≡ROWS BETWEEN CURRENT ROW AND CURRENT ROW(只算自己) -
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW→ 正确的滚动汇总起点 -
UNBOUNDED PRECEDING必须和BETWEEN ... AND ...搭配使用,单独写会语法报错
ROWS和RANGE下UNBOUNDED PRECEDING行为差异极大
看起来都是“从开头开始”,但覆盖的行数可能天差地别,尤其当排序列有重复值或时间不连续时:
-
ROWS模式:严格按物理行序,UNBOUNDED PRECEDING真就是分区第一行,不跳空、不合并重复 -
RANGE模式:按排序值扩展,UNBOUNDED PRECEDING表示“所有排序值 ≤ 当前行排序值”的行;某天缺数据,它仍会把之前所有存在的日期行全包进来 - 销售表缺某天记录,用
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW依然跳过那天——这不是UNBOUNDED PRECEDING的问题,而是ROWS本身不感知时间语义
UNBOUNDED PRECEDING只在分区内生效
这是最容易忽略的语义点:UNBOUNDED PRECEDING不是“全表第一行”,而是“当前分区的第一行”。一旦用了PARTITION BY,每个分区独立计算滚动,互不影响。
- 例如按
PARTITION BY region ORDER BY sale_date做累计,北京分区的UNBOUNDED PRECEDING只指向北京的第一条记录,和上海无关 - 如果误以为它跨分区生效,累计值会严重高估
- 真正容易被忽略的是:你写的
UNBOUNDED PRECEDING到底落在ROWS还是RANGE框架里——这直接决定它是按行号数,还是按值域扫










