位置:首页 > SQL > SQL 中如何计算分组移动平均值?

SQL 中如何计算分组移动平均值?

时间:2026-08-25  |  作者:穿越地图的猫  |  阅读:0

在MySQL 8.0+中,A VG()与OVER()搭配是计算移动平均的不二之选。其中,需通过PARTITION BY进行分组,ORDER BY实现排序,以及ROWS BETWEEN 2 PRECEDING AND CURRENT ROW来精准把控窗口范围。要注意的是,MySQL不支持RANGE时间偏移,所以对于“过去7天”的情况,需要额外处理。

SQL中如何计算分组移动平均值?

MySQL 8.0+ 用 A VG() 配合 OVER() 窗口函数最直接

MySQL 5.7 及更早版本不支持窗口函数,强行用子查询或自连接算移动平均容易超时或出错;8.0+ 版本里,A VG() + OVER() 是唯一合理选择。关键不是“能不能算”,而是“怎么定义窗口范围”。

比如按时间排序、对每个用户计算最近 3 条记录的销售额移动平均:

SELECT
user_id,
sale_date,
amount,
A VG(amount) OVER (
PARTITION BY user_id 
ORDER BY sale_date 
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_a vg_3
FROM sales;
  • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 表示包含当前行和它前面 2 行(共 3 行),不是“过去 3 天”
  • PARTITION BY user_id 必须加,否则不同用户的记录会混在一起算
  • 如果 sale_date 有重复,ORDER BY 里要加唯一字段(如 id)避免非确定性排序

PostgreSQL 和 SQL Server 的语法几乎一致,但要注意 ROWSRANGE 的区别

需注意,ROWS 是按物理行数截取,而 RANGE 则是按排序值范围截取——这可是最容易出错的地方哦。就比如说,在PostgreSQL中,使用 RANGE BETWEEN INTERVAL '2 days' PRECEDING AND CURRENT ROW 是可行的,但在MySQL 8.0+ 中,却不支持 RANGE 搭配时间间隔使用。

  • 想算“过去 7 天内”的移动平均,MySQL 必须先生成日期序列或用自连接,不能靠 RANGE
  • PostgreSQL 支持 RANGE + INTERVAL,但性能通常比 ROWS 差,尤其数据量大时
  • SQL Server 2012+ 支持 ROWS,但不支持 RANGE 时间偏移,和 MySQL 一样受限

SQLite 不原生支持窗口函数,得用相关子查询硬扛

SQLite 3.25+ 虽然支持窗口函数,但很多生产环境仍在用旧版本(如 Android 系统自带 SQLite)。此时只能用相关子查询,但必须小心性能和边界行为:

SELECT
t1.user_id,
t1.sale_date,
t1.amount,
(SELECT A VG(t2.amount)
 FROM sales t2
 WHERE t2.user_id = t1.user_id
 AND t2.sale_date <= t1.sale_date
 AND t2.sale_date >= (
 SELECT MAX(t3.sale_date)
 FROM sales t3
 WHERE t3.user_id = t1.user_id
 AND t3.sale_date <= t1.sale_date
 ORDER BY t3.sale_date DESC
 LIMIT 1 OFFSET 2
 )
) AS moving_a vg_3
FROM sales t1;
  • 这个写法在 SQLite 中能跑,但每行都触发两次子查询,10 万行数据可能秒变秒级延迟
  • LIMIT 1 OFFSET 2 是为了找“往前数第 3 天”,但若某用户数据稀疏(比如隔周才一条),结果会错
  • 没有 PARTITION BY 的等价机制,WHERE 条件必须手动对齐分组字段

NULL 值和首尾几行的处理逻辑必须显式确认

所有数据库对窗口开头不足 N 行的情况,都默认只算实际存在的行(即前两行的移动平均是 1 行或 2 行的均值),不会补 NULL 或 0。但如果你业务要求“不满 3 行就返回 NULL”,就得自己加判断:

  • MySQL 可用 COUNT(*) OVER(...) 配合 CASE 过滤:CASE WHEN COUNT(*) OVER(...) < 3 THEN NULL ELSE A VG(...) OVER(...) END
  • 首行的移动平均值等于它自己,这不是 bug,是定义如此;但报表里常被误认为异常,需提前和业务方对齐
  • 如果原始数据本身含 NULLamountA VG() 会自动忽略它们——这点和聚合函数一致,但容易被忽略

移动平均本身不难,难的是窗口定义是否贴合业务场景、数据库版本是否兜底、以及 NULL 和边界值是否符合预期。别光看结果数字对不对,先盯住那行 ROWS BETWEEN ... 有没有写错。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多