ROWS按物理行数切片,RANGE按排序键值范围切片;前者严格计数,后者受重复值影响大,且必须有ORDER BY和可比较类型字段。

ROWS 和 RANGE 的语义差异必须看懂
窗口函数里 ROWS 和 RANGE 看似只差一个字,实际行为完全不同:前者按物理行数切片,后者按排序键值范围切片。比如对时间序列用 RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW,它会把所有落在最近 7 天内的记录都拉进来,哪怕中间有空缺日期;而 ROWS BETWEEN 7 PRECEDING AND CURRENT ROW 只取紧挨着的 7 行,不管这些行对应的时间跨度有多大。
ORDER BY 缺失时 RANGE 会报错
RANGE 要求必须有 ORDER BY 子句,且排序字段必须是可比较的数值或日期类型。如果漏写 ORDER BY,PostgreSQL 报 ERROR: RANGE is only supported with ORDER BY,SQL Server 直接不支持 RANGE(只认 ROWS)。MySQL 8.0+ 支持 RANGE,但若 ORDER BY 字段是字符串或布尔型,也会拒绝执行。
- 数值类字段(
INT、DECIMAL、TIMESTAMP)可用RANGE - 字符串字段(
VARCHAR)在多数引擎中不支持RANGE边界计算 - 没
ORDER BY就别想用RANGE,连语法检查都过不去
重复值场景下 RANGE 容易“吞掉”多行
当 ORDER BY 字段存在大量重复值(比如状态码、分类 ID),RANGE 会把所有相同值的行视为同一“点”,导致窗口实际包含远超预期的行数。例如按 status 排序后写 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,只要前面有 100 行 status = 1,当前这行就会带上全部 100 行——而 ROWS 只会严格按行号计数。
- 业务上需要“截至当前值”的聚合(如累计销量按价格分段),
RANGE更自然 - 需要稳定可控的窗口大小(如移动平均固定看前 5 天销售),优先选
ROWS+ 显式日期补全 - 用
RANGE前先SELECT COUNT(*) GROUP BY order_col检查重复程度
性能差异取决于索引和数据分布
ROWS 窗口基本靠顺序扫描+游标偏移,只要 ORDER BY 字段有索引,性能很稳;RANGE 需要对每个窗口做值区间查找,重复值越多、范围越宽,CPU 和临时内存开销越大。PostgreSQL 中 RANGE 窗口在大数据集上可能比等效 ROWS 慢 3–5 倍,尤其当 ORDER BY 字段无索引时。
- 高频查询且排序字段已建索引 →
ROWS和RANGE差距不大 - 窗口范围动态(如
INTERVAL '30 days')且数据稀疏 →RANGE实际扫描行数可能远少于ROWS - 生产环境上线前务必用真实数据量
EXPLAIN ANALYZE对比两者执行计划
真正难的不是语法,而是判断当前业务逻辑到底依赖“位置”还是“值域”——比如“过去 N 笔订单”是位置问题,用 ROWS;“过去 N 天内所有订单”是值域问题,用 RANGE 更安全。但一旦排序字段有大量重复,RANGE 就可能悄悄改变语义,这点容易被忽略。










