unbounded preceding 必须与 between ... and ... 搭配使用,仅在窗口帧起始位置有效;其行为在 rows(按物理行)和 range(按排序值域)下差异显著,影响累计计算准确性。

UNBOUNDED PRECEDING 不是独立语法,它必须和 BETWEEN ... AND ... 一起用,且只能出现在窗口子句的起始位置。单独写 UNBOUNDED PRECEDING 会报错,比如 SUM(x) OVER (ORDER BY dt UNBOUNDED PRECEDING) 是非法的。
为什么 UNBOUNDED PRECEDING 总要配 BETWEEN
窗口帧(frame)的定义必须是闭区间,SQL 标准强制要求使用 BETWEEN start AND end 形式。起点可以是 UNBOUNDED PRECEDING、n PRECEDING 或 CURRENT ROW,终点同理,但起点不能晚于终点。
-
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:从分区第一行算到当前行(默认行为,当只写ORDER BY时自动补上) -
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:按排序值“相等扩展”,可能包含多行相同值的记录 - 写成
ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING会直接报错:起点不能在终点之后
UNBOUNDED PRECEDING 在 ROWS 和 RANGE 下行为差异很大
看起来都是“从开头开始”,但实际覆盖行数可能完全不同,尤其当排序列有重复值或非连续时。
-
ROWS模式下,UNBOUNDED PRECEDING真的就是物理行位置的第一行,不跳过空值、不合并重复,严格按顺序计数 -
RANGE模式下,UNBOUNDED PRECEDING表示“所有排序值 ≤ 当前行排序值”的行 —— 如果ORDER BY dt中dt是日期,而某天缺失数据,RANGE仍会把当天之前所有存在的日期行全包进来;但ROWS只认行号,不会跨过空档 - MySQL 8.0+ 支持
RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW,但低版本不支持,此时强行用ROWS+ 日历表补全才是稳妥做法
常见误用:以为加了 UNBOUNDED PRECEDING 就能解决“累计求和断层”问题
比如销售表缺某天记录,用 SUM(sales) OVER (ORDER BY day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 依然会跳过那天,结果是“按有数据的行累计”,不是“按自然日累计”。这不是 UNBOUNDED PRECEDING 的问题,而是 ROWS 本身不感知时间语义。
- 真要按日滚动,得先 LEFT JOIN 一个完整日期序列,再用
RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW - 如果只用
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW,那只是最近 7 条记录,和日粒度无关 -
UNBOUNDED PRECEDING再“宽”,也补不了源数据缺失的逻辑维度
真正容易被忽略的是:你写的 UNBOUNDED PRECEDING 到底落在 ROWS 还是 RANGE 框架里 —— 这直接决定它是按行号数,还是按值域扫,而错误假设会导致累计值、移动平均、排名全部偏移。










