range在日期列上必须用interval,因其按值区间而非行偏移计算;不加interval会导致日期被当数值减法处理而错乱,加interval才能正确表达“过去n天”语义。

RANGE 不能直接用于任意数值列做“前N行”式计算,它只对 ORDER BY 列的值做逻辑范围匹配,且必须配合 INTERVAL 才能在日期列上正确表达“过去7天”这类语义。
为什么 RANGE 在日期列上必须用 INTERVAL
RANGE 的本质是“值区间”,不是“行偏移”。比如 RANGE BETWEEN 7 PRECEDING AND CURRENT ROW 在日期列上会被解释为“排序值减去7”,即把日期当成数字减7——'2025-03-10' - 7 得到 20250303(非标准日期),结果完全错乱。
只有加上 INTERVAL,数据库才知道这是时间单位运算:
-
RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW→ 正确:取当前行日期往前推7天的所有行 -
RANGE BETWEEN 7 PRECEDING AND CURRENT ROW→ 错误:按数值减法算,不适用于 DATE 类型
MySQL 8.0+ 中 RANGE + INTERVAL 的实际写法
MySQL 要求 INTERVAL 表达式必须显式带单位,且 ORDER BY 列必须是日期/时间类型(如 DATE、DATETIME)。以下写法才有效:
SELECT
order_date,
amount,
SUM(amount) OVER (
ORDER BY order_date
RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW
) AS rolling_7day_sum
FROM orders;
注意点:
-
order_date必须是DATE或DATETIME,不能是字符串或整数时间戳 -
INTERVAL 7 DAY不能写成INTERVAL '7' DAY(部分版本报错),推荐不加引号 - 若存在重复日期,RANGE 会把同一天所有行都纳入窗口;ROWS 则只按物理顺序取固定行数
RANGE 和 ROWS 在空缺日期场景下的行为差异
假设数据中缺少 '2025-03-05' 这天的记录:
-
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW:永远取最近7条物理记录(不管日期是否连续),可能跨过10天甚至更久 -
RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW:严格限定在[current_date - 6, current_date]的完整日期范围内,哪怕中间缺3天,也只算存在的那4天数据
所以做“自然日滚动统计”必须用 RANGE + INTERVAL;做“最近N笔订单统计”才该用 ROWS。
容易被忽略的兼容性陷阱
不是所有支持窗口函数的数据库都支持 RANGE ... INTERVAL:
- PostgreSQL:支持,语法一致
- SQL Server:不支持
INTERVAL,只能用ROWS或自行 JOIN 日历表模拟 - Oracle:支持但语法略有不同,需写
RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW(引号必需) - SQLite:不支持
RANGE与INTERVAL组合,仅支持ROWS
跨库迁移时,别只看 OVER() 是否存在,重点检查 RANGE BETWEEN ... INTERVAL 这一整段是否被目标引擎识别。










