订阅更新

SQL 窗口函数 ROWS 和 RANGE 有什么区别?

面试问 052 约 3 分钟读完 3 次阅读

本文目录
  1. 面试怎么回答
  2. ROWS:按行的位置移动
  3. RANGE:按排序值的范围移动
  4. 一个简单例子
  5. 选择时看三个问题
  6. 一个典型反例

面试怎么回答

ROWS 按物理行位置定义窗口,RANGE 按 ORDER BY 值的范围定义窗口。排序值重复时,ROWS 逐行推进,但必须补充唯一排序字段保证稳定;RANGE 会把相同排序值的 Peer Rows 一起纳入边界。逐笔累计通常选 ROWS,按时间点或值区间累计才考虑 RANGE,同时显式写出 Frame,并确认目标引擎对 RANGE 的具体支持。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

同样是计算累计金额,两条记录的排序时间完全相同。

一条 SQL 会逐行增长,另一条却会把同一时间的多条记录一次性都算进去。

这不是数据库随机行为,而是窗口边界不同:ROWS 和 RANGE 到底有什么区别?

ROWS 按物理行数定义窗口;RANGE 按 ORDER BY 值的范围定义窗口。遇到相同排序值时,两者可能产生不同结果。


ROWS:按行的位置移动

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

表示从分区第一行累计到当前物理行。

如果同一时间有两条记录,它们仍是两行。要得到稳定顺序,ORDER BY 最好加入唯一字段:

ORDER BY event_time, event_id

否则同值记录之间的先后顺序可能不稳定。

RANGE:按排序值的范围移动

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

关注的是当前 ORDER BY 值。同一个排序值的 Peer Rows 通常会被视为同一边界,一起包含在当前窗口中。

ROWS 与 RANGE 结果对比


一个简单例子

输入数据:

10:00  amount=100
10:00  amount=200
10:05  amount=50

使用 ROWS 并保证第一条在前时,累计值可能是:

100 → 300 → 350

使用 RANGE 按时间累计时,两条 10:00 记录属于同一个排序值范围,结果可能是:

300 → 300 → 350

所以 RANGE 更像“累计到当前时间点”,ROWS 更像“累计到当前这条记录”。


选择时看三个问题

业务边界是行还是值?

逐笔交易流水通常使用 ROWS;按日期、价格区间或时间点整体计算,才考虑 RANGE。

排序字段是否重复?

如果重复值很多,Peer Rows 会让 RANGE 结果成组变化。必须确认这符合业务预期。

数据库是否支持对应语法?

不同引擎对 RANGE 的数据类型、偏移写法和多排序列支持可能不同。不要依赖隐式默认窗口,最好显式写出 Frame。


一个典型反例

比如希望按交易流水逐笔计算账户余额,却只按秒级 event_time 排序并使用 RANGE。

同一秒的多笔交易会一起进入窗口,余额出现“批量跳变”,无法还原每一笔后的状态。

正确做法是使用 ROWS,并在时间后增加稳定唯一的流水号排序。反过来,如果业务要的是“截至这个时间点的总额”,RANGE 的 Peer 语义可能更符合需求。

先定义业务边界,再选择窗口 Frame。


刷题进度保存在当前浏览器