SQL移动平均必须用ROWS BETWEEN而非RANGE,因RANGE按值分组遇重复值会导致窗口边界漂移,而ROWS严格按物理行数滑动,确保“最近N条”的准确平均;如7日均值需ROWS BETWEEN 6 PRECEDING AND CURRENT ROW。

SQL移动平均为什么必须用ROWS BETWEEN而不是RANGE
因为RANGE按值分组,遇到重复数值会把同一值的所有行全算进来,导致窗口边界漂移;而ROWS BETWEEN严格按物理行数滑动,才能得到真正意义上的“最近N条”的平均值。
比如时间字段有重复(同一秒多条记录),用RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW可能拉回几十行数据,但ROWS BETWEEN 6 PRECEDING AND CURRENT ROW永远只取当前行往前6行+当前行共7行——这才是业务常说的“7日移动平均”。
ROWS BETWEEN语法中PRECEEDING和FOLLOWING的常见误用
计算移动平均时几乎只用ROWS BETWEEN N PRECEDING AND CURRENT ROW,极少用FOLLOWING。一旦写成BETWEEN CURRENT ROW AND 6 FOLLOWING,就变成“向后看”,结果无法用于实时报表(未来数据还没产生)。
-
N PRECEDING里的N是整数,不能是列名或表达式(如days_back列) -
CURRENT ROW不是可选的——省略它等价于UNBOUNDED FOLLOWING,窗口会无限扩展 - 想包含前N行+后M行?写成
ROWS BETWEEN N PRECEDING AND M FOLLOWING,但注意这会让首尾几行结果不稳定(开头没足够前驱,结尾没足够后继)
不同数据库对ROWS BETWEEN的支持差异
PostgreSQL、SQL Server、Oracle、Snowflake都完整支持;MySQL从8.0开始支持;SQLite 3.25+支持但不支持UNBOUNDED关键字(得写具体数字);老版本Hive需开启hive.windowing.enabled=true且仅支持ORDER BY带唯一键,否则排序不稳定会导致ROWS行为异常。
示例(标准写法):
SELECT
date,
sales,
AVG(sales) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS ma7
FROM daily_sales;
注意:ORDER BY必须存在,否则ROWS BETWEEN无意义;如果date有重复,建议加二级排序如ORDER BY date, id保证顺序确定。
性能陷阱:窗口函数在大数据量下变慢的两个原因
一是未建索引:窗口的ORDER BY字段没索引,数据库每次都要重新排序;二是窗口太宽:比如ROWS BETWEEN 365 PRECEDING AND CURRENT ROW在亿级表上会显著拖慢,因每行都要累加366个值。
- 先确认
ORDER BY字段有索引(复合索引要匹配排序顺序) - 避免在
WHERE之前用窗口函数——它会先全表计算再过滤,应尽量下推过滤条件 - 若只需近似移动平均,可考虑用物化视图或定时聚合表替代实时窗口计算
窗口定义看着简单,但ORDER BY的稳定性、索引覆盖、以及数据库版本对ROWS的实际实现,三者缺一都会让结果出人意料。










