移动平均值必须配合AVG() + OVER() + ROWS BETWEEN,且ROWS BETWEEN的起止偏移量决定滑动范围;错用RANGE或漏写ORDER BY会导致结果完全不对,关键是以CURRENT ROW为锚点,用N PRECEDING指定向前行数,如过去3天含当天需写ROWS BETWEEN 2 PRECEDING AND CURRENT ROW。

SQL里用ROWS BETWEEN算移动平均,核心是定义好窗口边界
直接说结论:移动平均值必须配合AVG() + OVER() + ROWS BETWEEN,且ROWS BETWEEN的起止偏移量决定滑动范围。错用RANGE或漏写ORDER BY会导致结果完全不对。
ROWS BETWEEN的起始和结束位置怎么填才对
关键在理解“当前行”是参考点,偏移量是相对于它的行数。比如过去3天(含当天)的平均,就是ROWS BETWEEN 2 PRECEDING AND CURRENT ROW——因为要往前数2行,加上自己共3行。
-
N PRECEDING:当前行往上数N行(含),N必须是非负整数 -
CURRENT ROW:就是当前这行,不是“从当前开始”,它只是个锚点 -
M FOLLOWING:当前行往下数M行(含),一般少用,除非补未来数据 - 错误写法:
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING看似顺,但若排序字段有重复值,行序可能不稳定,结果不可靠
为什么加了ROWS BETWEEN还是报错或结果为空
常见三个硬性前提没满足:窗口函数必须有ORDER BY;表/查询结果必须能明确排序;分区字段(PARTITION BY)如果用了,得确认分组内行数足够撑起窗口大小。
- 没
ORDER BY:直接报错,如Window definition requires ORDER BY - 排序字段有大量
NULL:NULL默认排最前,导致前几行窗口实际只包含NULL,AVG()返回NULL - 窗口太大:比如
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW,但某组前5行不存在,那这行结果就是AVG()作用于少于5行的数据(合法,但得意识到这点) - 示例:
SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS ma3 FROM sales_data
ROWS BETWEEN和RANGE BETWEEN混用会出什么问题
ROWS按物理行数滑动,RANGE按排序字段值的逻辑区间滑动。同一日期有多条记录时,RANGE BETWEEN INTERVAL '2 days' PRECEDING AND CURRENT ROW可能拉进10行,而ROWS BETWEEN 2 PRECEDING AND CURRENT ROW永远只取3行——两者语义完全不同,不能互换。
- 时间类移动平均优先用
ROWS,可控、稳定、性能好 -
RANGE适合需要“值对齐”的场景,比如股价按价格区间而非交易次数统计 - MySQL 8.0+、PostgreSQL、SQL Server 都支持
ROWS,但旧版MySQL不支持窗口函数,别踩坑
真正容易被忽略的是排序稳定性:即使写了ORDER BY date,如果date字段精度只到天,又没加二级排序(比如ORDER BY date, id),数据库可能任意打乱同天多行的顺序,导致ROWS BETWEEN每次执行结果不一致。










