ROWS按物理行数切片,RANGE按排序键值范围切片;前者严格计数,后者受重复值影响大,且必须有ORDER BY和可比较类型字段。
窗口函数里 ROWS 和 RANGE 看似只差一个字,实际行为完全不同:前者按物理行数切片,后者按排序键值范围切片。比如对时间序列用 RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW,它会把所有落在最近 7 天内的记录都拉进来,哪怕中间有空缺日期;而 ROWS BETWEEN 7 PRECEDING AND CURRENT ROW 只取紧挨着的 7 行,不管这些行对应的时间跨度有多大。
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,连语法检查都过不去当 ORDER BY 字段存在大量重复值(比如状态码、分类 ID),RANGE 会把所有相同值的行视为同一“点”,导致窗口实际包含远超预期的行数。例如按 status 排序后写 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,只要前面有 100 行 status = 1,当前这行就会带上全部 100 行——而 ROWS 只会严格按行号计数。
RANGE 更自然ROWS + 显式日期补全RANGE 前先 SELECT COUNT(*) GROUP BY order_col 检查重复程度ROWS 窗口基本靠顺序扫描+游标偏移,只要 字段有索引,性能很稳;
RANGE 需要对每个窗口做值区间查找,重复值越多、范围越宽,CPU 和临时内存开销越大。PostgreSQL 中 RANGE 窗口在大数据集上可能比等效 ROWS 慢 3–5 倍,尤其当 ORDER BY 字段无索引时。
ROWS 和 RANGE 差距不大INTERVAL '30 days')且数据稀疏 → RANGE 实际扫描行数可能远少于 ROWS
EXPLAIN ANALYZE 对比两者执行计划真正难的不是语法,而是判断当前业务逻辑到底依赖“位置”还是“值域”——比如“过去 N 笔订单”是位置问题,用 ROWS;“过去 N 天内所有订单”是值域问题,用 RANGE 更安全。但一旦排序字段有大量重复,RANGE 就可能悄悄改变语义,这点容易被忽略。