MySQL 8.0+ 中 AVG() 窗口函数最稳妥,需显式指定 ORDER BY 和 ROWS BETWEEN(如 2 PRECEDING AND CURRENT ROW),配合 PARTITION BY 实现分组内独立计算,NULL 自动忽略,且不可嵌套窗口。

MySQL 8.0+ 直接用 AVG() 窗口函数最稳妥
MySQL 5.7 及更早版本不支持窗口函数,强行用子查询或自连接算移动平均极易卡死或结果错乱。8.0+ 版本里,AVG() 配合 ORDER BY 和 ROWS BETWEEN 是唯一推荐路径。
- 必须显式指定
ORDER BY—— 窗口函数依赖排序,没它结果无意义 - 移动窗口范围写法要精确:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示含当前行共3期;ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING则向后看 - 分组后每个组内独立计算:先
GROUP BY,再在窗口定义里用PARTITION BY(注意不是外层 GROUP BY) - NULL 值会被自动忽略 —— 这是
AVG()的默认行为,不用额外处理
PostgreSQL 用 AVG() OVER() 更灵活
PostgreSQL 对窗口函数支持更成熟,ROWS 和 RANGE 两种模式都可用。但要注意:RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW 这类按时间范围的写法,在 PostgreSQL 中要求排序字段是日期类型且不能有重复值,否则可能漏行。
- 如果按时间做滑动窗口,优先用
ROWS+ 序号(如ROW_NUMBER()),比RANGE更可控 -
PARTITION BY必须放在ORDER BY前面,顺序错了会报错:ERROR: window definition has ORDER BY but no PARTITION BY - 聚合窗口函数不支持嵌套窗口,比如不能在
AVG() OVER(...)里再套一个SUM() OVER(...)
SQL Server 的 AVG() OVER() 要小心数据类型隐式转换
SQL Server 默认把整数列的平均值转成 int,导致小数位全丢。例如对 [sales](int 类型)直接算移动平均,结果全是整数,偏差可能很大。
- 必须显式转换:把列转成
FLOAT或DECIMAL(10,2),例如AVG(CAST(sales AS FLOAT)) OVER(...) -
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW是累计平均,不是移动平均——别混淆 - 如果分区键有大量重复值,且用了
RANGE模式,SQL Server 会把所有同值行当做一个“逻辑行”,窗口实际跨度可能远超预期
SQLite 不原生支持,别硬套窗口函数
SQLite 3.25+ 虽然加了窗口函数,但 AVG() OVER() 在某些旧发行版(如 macOS 自带 sqlite3 3.19)里根本不可用,运行时报 no such function: AVG。强行用自连接模拟 N 日移动平均,N 超过 5 就明显变慢。
- 确认版本:
SELECT sqlite_version();—— 必须 ≥ 3.25 才能用 - 替代方案:导出数据到 Python/Pandas 里用
rolling().mean(),比硬写 SQL 更快更准 - 如果非得在 SQLite 里跑,用 CTE +
LIMIT模拟前 N 行,但只能固定步长,无法动态调整窗口大小
ORDER BY 或排序字段选错(比如用 id 代替 created_at),结果就完全不可信。

















