ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 定义滚动7行窗口,不依赖日期连续性,仅按排序取当前行及前6行;需稳定ORDER BY(如加order_id防重复),N必须为整数常量,首6行结果自然不完整;真·7自然日需用RANGE + INTERVAL。

ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 是滚动7天的物理行定义
它不看日期是否连续,只取排序后当前行往前数6行(含当前行共7行)。比如某天没数据,窗口会跨过空缺日,从更早的记录里补足7条——这适合“最近7笔交易”类场景,但不是自然日意义上的“7天”。
关键点:
-
ORDER BY必须存在且稳定:如果时间字段有重复(如同一秒多条订单),得加二级排序,例如ORDER BY order_date, order_id -
CURRENT ROW不能省略:省略等价于UNBOUNDED FOLLOWING,窗口会一路往后扩,结果完全失控 -
N PRECEDING的N必须是整数常量:不能写成days_back列或@n变量,MySQL/PostgreSQL 都不支持动态行数 - 首尾几行结果天然不完整:第1行只有自己,第2行只有前1行+自己……直到第7行才凑满7条;这是正常行为,不是 bug
RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW 才是真·7自然日
如果你要的是“过去7个自然日内所有记录的平均值”,必须用 RANGE + INTERVAL,且 ORDER BY 列必须是 DATE 或 DATETIME 类型。
常见陷阱:
- 写成
RANGE BETWEEN 7 PRECEDING AND CURRENT ROW:数据库会把日期当数字减7,'2025-03-10'变成20250303,结果全错 - MySQL 要求
INTERVAL不带引号:INTERVAL 7 DAY可行,INTERVAL '7' DAY在某些版本报错 - 日期字段是字符串或时间戳(如
INT):RANGE+INTERVAL直接失效,必须先转成DATE - 同一天多条记录:
RANGE会全部纳入窗口,ROWS只按顺序取固定条数——这是设计差异,不是缺陷
为什么不能混用 ROWS 和 RANGE 来“兼顾行数和日期”
SQL 标准规定:一个窗口框架只能选 ROWS 或 RANGE,不能同时指定。想“取最近7天内最多10条记录”这种逻辑,原生窗口函数做不到,必须用子查询或 CTE 先过滤再开窗。
典型替代方案:
- 先用
WHERE order_date >= CURRENT_DATE - INTERVAL '7' DAY缩小数据集,再在结果上套ROWS BETWEEN 9 PRECEDING AND CURRENT ROW - 或用
ROW_NUMBER() OVER (PARTITION BY DATE(order_date) ORDER BY order_time)给每日记录编号,再外层筛选 - 注意:这类组合操作会让执行计划变复杂,大数据量时需加
order_date索引
不同数据库对 ROWS BETWEEN 的兼容性细节
主流数据库基本都支持,但边缘情况容易翻车:
- SQLite 3.25+ 支持
ROWS,但不认UNBOUNDED,得写成具体大数,比如ROWS BETWEEN 1000000 PRECEDING AND CURRENT ROW - Hive 老版本(set hive.windowing.enabled=true,且
ORDER BY字段必须唯一,否则ROWS行为不可预测 - MySQL 8.0+ 支持完整语法,但
AVG()窗口函数在NULL值处理上和 PostgreSQL 略有差异:MySQL 默认跳过NULL,PostgreSQL 同样,但有些 BI 工具连接层会额外做空值填充
最易被忽略的是排序稳定性——哪怕语法全对,ORDER BY 缺少唯一键时,同一组数据多次执行可能返回不同窗口组成,尤其在分布式引擎(如 Presto、Trino)上更明显。

















