ROWS比RANGE快,因行号可直接寻址而值范围需重复扫描;ROWS是O(1)随机访问,RANGE最坏O(n²),尤其在ORDER BY列存在大量重复值时;多数数据库默认隐式使用RANGE,易致性能断崖下跌且无警告。

ROWS比RANGE快,根本原因是行号可直接寻址,值范围要重复扫描
ROWS模式下,数据库只需按ORDER BY排好序后,用游标偏移固定行数(比如2 PRECEDING),物理跳转即可定位窗口边界——本质是数组下标运算,O(1)随机访问。RANGE则必须在排序后的数据流中,对每个当前行,重新向左/右线性扫描所有满足“值在指定区间内”的行,最坏情况是O(n)每行,整体退化为O(n²)。
尤其当ORDER BY列存在大量重复值(如日志表的date字段、状态码字段),RANGE会把所有同值行打包处理,但内部仍需逐行比对边界条件,无法跳过;而ROWS完全无视值内容,只数行数。
RANGE在隐式框架下容易“悄悄变慢”,且不报错
多数数据库(PostgreSQL/MySQL/SQL Server)默认未显式写ROWS或RANGE时,自动补上RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。这意味着你以为写了SUM(x) OVER (ORDER BY ts)只是简单累计,实际却触发了RANGE逻辑——如果ts有毫秒级重复,或用了DATE(ts)导致大批日期相同,性能会断崖下跌,但SQL语法完全合法,无任何警告。
- 查执行计划时注意
WindowAgg节点是否带Range字样(PostgreSQL)或Sort + Window双阶段(SQL Server) - 用
EXPLAIN ANALYZE对比加ROWS前后的Actual Total Time,差异常达3–10倍 - 重复值越多,RANGE越慢;ROWS基本不受重复值影响
什么时候必须用RANGE?别硬套ROWS
不是所有场景都能用ROWS替代RANGE。以下需求RANGE不可替代:
- 按时间区间聚合:比如
SUM(revenue) OVER (ORDER BY event_time RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW)——“过去7天内所有订单”,这里天数是逻辑跨度,不是固定行数 - 按数值区间对标:比如
AVG(price) OVER (ORDER BY price RANGE BETWEEN 50 PRECEDING AND 50 FOLLOWING)——“价格±50元内的竞品均价” - 业务定义的“同等级”累计:如按
score_level分档(A/B/C),要求“所有同等级及更高等级的总人数”,这时RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING才符合语义
强行改用ROWS会导致结果错误:比如7天内有120笔订单,你设ROWS BETWEEN 120 PRECEDING,但某天集中爆发200单,就漏掉80单;反之低峰期又会多拉无关行。
ORDER BY列含NULL或非确定性函数,会让两者都失效
无论选ROWS还是RANGE,只要ORDER BY列出现以下情况,窗口行为就不可控:
-
NULLS FIRST/NULLS LAST未显式声明:不同数据库默认策略不同,导致同一SQL在PG和MySQL里结果不一致 - 用了
RANDOM()、NOW()、CURRENT_TIMESTAMP等非确定性函数:每次执行排序结果不同,窗口边界漂移,SUM/AVG等结果随机波动 - 排序列类型隐式转换:比如
ORDER BY created_at::date产生大量重复值,RANGE效率骤降,ROWS虽快但语义已偏移(按日期分组 vs 按原始时间戳分组)
真正稳定的写法是:先用子查询生成确定性序号(如ROW_NUMBER() OVER (ORDER BY ts, id)),再在外层按该序号开窗——这样既规避了NULL和非确定性问题,又让ROWS能安全使用。


















