支持,MySQL 8.0 用 AVG() OVER (ORDER BY ... ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) 可实现严格 5 日移动平均;需确保 ORDER BY 包含唯一列防排序漂移,且窗口内 NULL 被自动忽略、不补零。

MySQL 8.0 窗口函数支持移动平均吗?
支持,但必须用 AVG() 配合 ROWS BETWEEN 显式定义滑动窗口范围;直接写 AVG(col) OVER (ORDER BY t) 默认是累积平均(从第一行到当前行),不是移动平均。
怎么写一个真正的 5 日移动平均?
关键在 ROWS BETWEEN 4 PRECEDING AND CURRENT ROW —— 它表示“包含当前行在内的最近 5 行”。注意:PRECEDING 数值比窗口长度小 1,所以 5 日要写 4。
常见错误现象:ROWS BETWEEN 5 PRECEDING AND CURRENT ROW 实际取 6 行;时间字段有重复值时,ORDER BY 若没加唯一键(如主键),会导致排序不稳定,同一查询多次结果不一致。
实操建议:
- 确保
ORDER BY子句包含足够唯一性,例如ORDER BY date ASC, id ASC - 若原始数据按日期聚合过(如每日一条),且存在缺失日期,需先用
LEFT JOIN补全日期序列,否则窗口会跳过空缺日、导致跨度不准 - 对
NULL值敏感:窗口内只要有一行col为NULL,AVG()会自动忽略它(符合 SQL 标准),但行数计数仍按ROWS范围算
示例(假设表 sales 有 sale_date 和 amount):
SELECT
sale_date,
amount,
ROUND(AVG(amount) OVER (
ORDER BY sale_date ASC, id ASC
ROWS BETWEEN 4 PRECEDING AND CURRENT ROW
), 2) AS ma5
FROM sales;
ORDER BY 中漏掉唯一列会出什么问题?
MySQL 8.0 不保证相同 ORDER BY 值的行物理顺序。如果只写 ORDER BY sale_date,而某天有多条记录,每次执行可能得到不同窗口组成,导致 ma5 值漂移 —— 这类 bug 很难复现,但线上会出现计算结果不一致。
性能影响:添加唯一列(如自增 id)几乎不增加开销,但能彻底避免非确定性排序;若表无合适唯一列,可考虑用 ROW_NUMBER() OVER (...) AS rn 生成临时序号再参与排序。
移动平均值为空或为 0 的常见原因
不是语法错,而是语义陷阱:
-
AVG(NULL)返回NULL,不是 0;若首几行窗口不足 5 行(如第 1 行只有自己),AVG()仍正常计算该范围内非NULL值的平均,不会补零 - 如果想让前 4 行也显示
NULL(即严格要求满 5 行才计算),改用ROWS BETWEEN 4 PRECEDING AND CURRENT ROW+CASE WHEN COUNT(*) OVER (...) < 5 THEN NULL ELSE ... END - 字段类型为
INT时,AVG()默认返回DECIMAL,但若参与后续计算未显式CAST,可能因精度截断导致看起来像“变 0”
真正容易被忽略的是:窗口函数在 WHERE 之后执行,所以过滤掉的数据不会进入窗口计算 —— 如果你先 WHERE status = 'done',那移动平均只基于已过滤的行滑动,不是原始全量时间序列。


















