ROWS BETWEEN 是窗口函数的帧定义语法,非独立函数;7日移动平均需用 AVG() OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW),前提是日期严格排序且无缺失。

ROWS BETWEEN 是窗口函数的帧定义语法,不是独立函数
很多人搜“ROWS BETWEEN 计算移动平均”,结果卡在语法报错,是因为误以为 ROWS BETWEEN 本身能算平均——它只是告诉 AVG() “从哪几行取数”。真正干活的是 AVG() 窗口函数,ROWS BETWEEN 只负责划范围。
7日移动平均要求:对当前行及往前6行(共7天)的数值求均值。前提是数据按日期严格排序,且每天一条记录(无缺失)。如果日期有空缺,得先补全或改用 RANGE BETWEEN + 时间偏移(但兼容性差,下文不展开)。
实操建议:
- 必须搭配
ORDER BY date_column使用,否则ROWS BETWEEN行为未定义 -
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW才是标准7日回溯写法(0 preceding 是当天,1 preceding 是昨天……6 preceding 是7天前) - 如果表里存在多条同日期记录,
ROWS按物理行数算,可能跨天;此时应先按日期聚合,再开窗
正确写法:AVG() OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
这是最常用、兼容性最好的写法,主流数据库(PostgreSQL、SQL Server、Oracle、Snowflake、DuckDB)都支持。MySQL 8.0+ 也支持。
示例(假设表叫 sales,字段为 sale_date 和 amount):
SELECT sale_date, amount, ROUND(AVG(amount) OVER (ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), 2) AS avg_7d FROM sales ORDER BY sale_date;
注意点:
-
ROUND()不是必须的,但原始浮点结果往往太长,实际使用中几乎都会加 - 第一行只能算自身,第二行算两天均值……第7行才满7日;前6行的
avg_7d值天然偏小,这是正常现象,别误以为逻辑错了 - 如果想让前6行也强制用7日(比如拿首日重复填充),得额外用
CASE WHEN ROW_NUMBER() OVER (...) 处理,但违背移动平均本意
常见错误:把 CURRENT ROW 写成 FOLLOWING 或搞反方向
典型报错或结果异常,90% 出在这里。比如写成 ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING,算的是“未来7天”,和移动平均需求相反;或者漏掉 CURRENT ROW,只写 6 PRECEDING,语法直接报错。
关键规则:
-
PRECEDING= 往上(时间早),FOLLOWING= 往下(时间晚) -
ROWS BETWEEN X PRECEDING AND Y FOLLOWING中,X 和 Y 都是非负整数,且至少一边必须是CURRENT ROW或明确数字 - 移动平均只用
PRECEDING + CURRENT ROW,不用FOLLOWING - 别写
7 PRECEDING AND CURRENT ROW——那是8天,不是7日
日期有缺失时,ROWS BETWEEN 会出问题
如果数据里缺了某天(比如没销售记录的周末),ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 仍会取最近7行,但可能跳过实际日期间隔。例如:周五记录后直接接周一,中间三行缺失,那“7行”实际只覆盖4个自然日。
这时不能靠 ROWS 解决,需换思路:
- 先用生成序列或左连接补全所有日期(较重,但最可控)
- 用
RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW(仅 PostgreSQL、Snowflake 支持,MySQL 不支持) - 业务层处理:导出后用 Python/Pandas 按真实日期重采样(更灵活,尤其涉及节假日调整时)
真正难的不是写对那一行 SQL,而是确认你的“7日”到底指日历日还是交易日、有没有节假日豁免、缺失数据要不要插值——这些决定了你该用 ROWS 还是绕开它。

















