rows按行号跳转,o(1);range需逐行重扫值边界,最坏o(n²),重复值越多性能越差;默认隐式补range,易被忽略;mysql对range+日期有硬限制,须转整数列。

ROWS靠行号跳转,RANGE要逐行重扫值边界
ROWS模式下,数据库排好序后直接用游标偏移定位窗口上下界——比如ROWS BETWEEN 2 PRECEDING AND CURRENT ROW,就是“当前行往前数2行”,本质是数组下标运算,O(1)时间复杂度。RANGE则不同:对每一行都要重新扫描整个已排序序列,找出所有满足“ORDER BY列值在指定区间内”的行,最坏情况每行都得线性遍历,整体退化为O(n²)。
重复值越多,RANGE性能越崩
当ORDER BY列存在大量重复(如DATE(created_at)、status、毫秒级时间截断到秒),RANGE会把同值行打包处理,但内部仍需逐行比对边界条件,无法跳过;而ROWS完全无视值内容,只数行数。典型现象:RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW在某天有200条日志时,这200行每行都要重查“哪些行的date落在7天内”,CPU和I/O压力陡增。
默认隐式补RANGE,不报错也不提醒
PostgreSQL/MySQL 8.0+/SQL Server全默认补RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。你写SUM(x) OVER (ORDER BY ts)看着简洁,实际跑的是RANGE逻辑。执行计划里WindowAgg节点带Range字样(PostgreSQL)或出现Sort + Window双阶段(SQL Server),基本就是它在拖慢。MySQL的EXPLAIN若显示Using filesort,说明没走索引,RANGE会雪崩。
MySQL对RANGE+日期的支持有硬限制
MySQL 8.0+不支持RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW这种写法,直接报ERROR 3589 (HY000): Window frame 'RANGE' with offset must be of type numeric。必须先转成整数列,比如TO_DAYS(date_col),再用RANGE BETWEEN 6 PRECEDING AND CURRENT ROW。否则要么改用ROWS(但语义可能错),要么用子查询模拟,性能更差。
EXPLAIN ANALYZE对比Actual Total Time,根本意识不到RANGE慢了3–10倍——尤其当业务刚上线、数据分布还不均匀时,这个问题常被当成“偶发抖动”忽略。










