ROWS按物理行数定义窗口,RANGE按排序值逻辑范围定义窗口;ROWS严格计数行号,RANGE聚合同值组并按值域扩展,选型依“固定数量”或“值域区间”需求而定。

ROWS 和 RANGE 都是用来定义窗口函数中“当前行的邻居范围”,但它们的划分逻辑完全不同:ROWS 按物理行数切分,RANGE 按排序值的逻辑范围切分。
ROWS 是按行号计数的“固定长度滑动窗口”
ROWS 严格依据 ORDER BY 后的排序结果,从当前行出发,向上(PRECEDING)或向下(FOLLOWING)数指定数量的物理行。即使这些行的排序值相同,也各自独立计数。
- 例如 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 总是取当前行前后各 1 行,共 3 行,不管这三行的 ORDER BY 列值是否相等
- 如果当前行是第 5 行,且前面有重复值,ROWS 仍只拉第 4、5、6 行,不会因为第 3 行和第 4 行值相同就多拉一行
- 适用于需要“最近 N 笔交易”“前后 N 天记录”这类明确数量控制的场景
RANGE 是按排序值分组的“值域区间窗口”
RANGE 把 ORDER BY 列中值相等的所有行视为一个逻辑单元(即“同值组”),然后以当前行的排序值为中心,向左右扩展指定的值范围(需配合 UNBOUNDED 或数字常量使用),所有落在该值范围内的行都会被纳入窗口。
- 例如 RANGE BETWEEN 10 PRECEDING AND CURRENT ROW 表示:取排序列值在 [当前行值 − 10, 当前行值] 区间内的所有行
- 若 ORDER BY 列是金额,当前行为 100,则会包含所有金额在 90 到 100 之间的行(哪怕有 20 行都等于 95,全算进来)
- 注意:RANGE 默认仅支持 ORDER BY 单一数值列(如 INT/FLOAT/DATE),不支持字符串或多个列
当 ORDER BY 值重复时,ROWS 和 RANGE 行为差异最明显
假设有以下按 score 排序的数据:
id | score 1 | 80 2 | 85 3 | 85 4 | 85 5 | 90
对每一行计算 COUNT(*) OVER (ORDER BY score ...)
- 用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:每行的计数依次是 1,2,3,4,5(严格按行号累加)
- 用 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:第 1 行得 1;第 2–4 行(score=85)都看到“从最小值到 85”的全部行,结果都是 4;第 5 行才变成 5
实际选型建议
- 要“取最近 3 条记录”,选 ROWS;要“取价格相差不超过 5 的所有商品”,选 RANGE
- 涉及时间字段(如 order_date)时,ROWS 不适合表达“过去 7 天”,而 RANGE 可写 RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW(PostgreSQL/Oracle 支持,MySQL 8.0+ 对 DATE 类型有限支持)
- 默认框架(未显式写 ROWS/RANGE)在多数数据库中等价于 RANGE UNBOUNDED PRECEDING,这点容易踩坑,建议始终显式声明










