range between 不是按行数滑动的工具,因其本质是按值域(value-based)划分窗口边界,依据 order by 列的值大小匹配区间(如 [当前值−2, 当前值]),而非物理行号;故窗口行数不固定,遇重复值会全部纳入导致膨胀,且数据库支持差异大(postgresql/sql server 支持 interval,mysql 仅支持数值偏移)。

为什么 RANGE BETWEEN 不是“按行数滑动”的工具
RANGE BETWEEN 的本质是按**值域(value-based)** 划分窗口边界,不是按物理行数。它依赖 ORDER BY 列的值大小,而非 ROWS BETWEEN 那样数第几行。所以当你写 RANGE BETWEEN 2 PRECEDING AND CURRENT ROW,数据库不会找前两行,而是找 ORDER BY 列值在 [当前值 - 2, 当前值] 区间内的所有行——如果该列是时间戳或金额,结果行数完全不固定。
常见错误现象:SELECT val, AVG(val) OVER (ORDER BY ts RANGE BETWEEN INTERVAL '1 DAY' PRECEDING AND CURRENT ROW) 在某天数据密集时返回 50 行窗口,空档日则只剩 1 行;但若误用 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW,就变成严格两行平均,和业务意图脱节。
- 必须确保
ORDER BY列有明确、可比较的语义(如ts、amount),不能是id这类无序代理键 -
RANGE对重复值敏感:多个相同ORDER BY值会全部被纳入同一窗口,导致窗口突然膨胀 - PostgreSQL 和 SQL Server 支持
INTERVAL,MySQL 8.0+ 仅支持数值型偏移(如RANGE BETWEEN 100 PRECEDING AND CURRENT ROW),需提前单位换算
如何用 RANGE BETWEEN 实现“最近 N 小时”滚动平均
典型场景:对传感器时间序列做每条记录的“过去 3 小时均值”。关键不是“过去 3 行”,而是“过去 3 小时内所有采集点”。
实操建议:
- 确认时间列类型是
TIMESTAMP或DATE,且索引已建在(sensor_id, ts)上,否则性能急剧下降 - PostgreSQL 示例:
AVG(value) OVER (PARTITION BY sensor_id ORDER BY ts RANGE BETWEEN INTERVAL '3 HOURS' PRECEDING AND CURRENT ROW) - MySQL 8.0+ 不支持
INTERVAL,需转为秒数:RANGE BETWEEN 10800 PRECEDING AND CURRENT ROW(假设ts是 Unix 时间戳整数) - 注意时区:若
ts是TIMESTAMP WITH TIME ZONE,INTERVAL计算自动适配;若为DATETIME,需统一转为 UTC 再计算
RANGE 遇到重复排序值时的意外行为
当 ORDER BY 列存在重复值(比如多条记录同属一秒级时间戳),RANGE BETWEEN 会把所有这些重复值全包进窗口——哪怕它们物理位置相隔很远。这常导致窗口远大于预期。
例如:ORDER BY event_time 下有 10 条 event_time = '2024-01-01 10:00:00' 的记录,执行 RANGE BETWEEN 1 PRECEDING AND CURRENT ROW 时,只要前一条 event_time 是 '2024-01-01 09:59:59',这 10 条就会全部出现在每个窗口里。
- 验证方式:加
COUNT(*) OVER (...)看实际窗口行数,别信理论偏移量 - 缓解方法:在
ORDER BY中追加唯一列,如ORDER BY event_time, id,但注意这会让RANGE退化为近似ROWS行为(因id值域远大于时间差) - 更稳妥做法:改用
ROWS BETWEEN+ 子查询预聚合,或用LATERAL JOIN显式控制范围
不同数据库对 RANGE BETWEEN 的支持差异
不是所有方言都允许任意表达式。兼容性陷阱比想象中多。
- PostgreSQL:支持
INTERVAL、数值、甚至自定义类型(需定义+/-操作符) - SQL Server:只支持数值型偏移,且
ORDER BY列必须是数值或日期,不支持字符串RANGE - MySQL 8.0+:仅支持整数偏移,且要求
ORDER BY列为数值类型;TIMESTAMP必须转成 Unix 时间戳才能用RANGE - BigQuery:支持
INTERVAL,但语法为RANGE BETWEEN 3 HOUR PRECEDING AND CURRENT ROW(无INTERVAL关键字)
跨平台时,最安全的底线是:把业务逻辑中的“时间范围”“金额区间”全部显式转换为数值单位,再用 RANGE BETWEEN N PRECEDING AND CURRENT ROW,并做好类型校验。











