如何在SQL中计算移动平均值并结合GROUP BY使用?

千丽姑娘_1853

千丽姑娘_1853

2026-09-18

809人浏览

原创

sql中直接用avg()窗口函数配合rows between子句即可实现移动平均,需显式order by确保行序稳定,如rows between 2 preceding and current row计算含当前行共3行的均值,且必须避免排序字段重复导致窗口错位。

如何在sql中计算移动平均值并结合group by使用?

SQL里直接用AVG()窗口函数加ROWS BETWEEN就能算移动平均

标准SQL(PostgreSQL、SQL Server、Oracle、BigQuery、MySQL 8.0+)都支持AVG()作为窗口函数,配合ROWS BETWEEN子句定义滑动窗口范围。关键不是写GROUP BY,而是先按时间/序号排序,再开窗——GROUP BY是后续聚合维度,和移动平均本身不冲突。

常见错误是试图在GROUP BY后套用AVG(),结果得到的是分组内静态均值,不是“每行往前看N期”的移动平均。正确做法是:先用ORDER BY确定序列顺序,再用ROWS BETWEEN N PRECEDING AND CURRENT ROW限定窗口。

  • 必须有明确的排序字段(比如order_dateid),否则ROWS BETWEEN行为不可靠
  • 如果排序字段有重复值,建议加二级排序(如ORDER BY order_date, id)避免非确定性结果
  • MySQL 5.7及更早版本不支持窗口函数,强行用会报错ERROR 1064

GROUP BY和移动平均要分两层处理:先开窗,再分组聚合

想按产品类别看每个类别的销售移动平均?不能把GROUP BY category和窗口函数混在同一层SELECT里——窗口函数在GROUP BY之后执行,但它的计算依赖原始行粒度。正确结构是:子查询或CTE中先算出每行的移动平均,外层再按category聚合(比如取每个类别的最新移动均值、或平均移动均值)。

例如,要获取每个category下最近3天销售额的移动平均(按日期滚动),得这样写:

来画
来画

来画是一款AI文本写作工具,AI漫剧全网内测 创作不再受限。

下载
SELECT 
  category,
  AVG(ma_3d) AS avg_of_ma_per_category
FROM (
  SELECT 
    category,
    sale_amount,
    AVG(sale_amount) OVER (
      PARTITION BY category 
      ORDER BY sale_date 
      ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS ma_3d
  FROM sales
) t
GROUP BY category;
  • PARTITION BY category确保窗口只在同类数据内滑动,不会跨类别“借数”
  • 外层GROUP BY是对已算好的移动平均值做二次统计,不是重算移动平均
  • 如果漏掉PARTITION BY,所有类别的数据会混在一起排序滑动,结果完全失真

SQLite和旧版MySQL用户得用自连接或子查询模拟窗口

SQLite直到3.25.0才支持窗口函数,且ROWS BETWEEN语法受限;MySQL 5.7及之前版本只能靠关联查询硬凑。性能差、写法绕,但能用。

以MySQL 5.7为例,算每个id往前2行的移动平均(假设表按id自然递增):

SELECT 
  t1.id,
  t1.value,
  (t1.value + COALESCE(t2.value, 0) + COALESCE(t3.value, 0)) / 
    (1 + IF(t2.value IS NOT NULL, 1, 0) + IF(t3.value IS NOT NULL, 1, 0)) AS ma_3
FROM data t1
LEFT JOIN data t2 ON t2.id = t1.id - 1
LEFT JOIN data t3 ON t3.id = t1.id - 2
ORDER BY t1.id;
  • 必须保证id连续且无缺失,否则t2.id = t1.id - 1会跳过空缺位置
  • COALESCE和动态计数避免除零,但逻辑易错,尤其边界行(前两行)结果可能不准
  • 数据量稍大(>10万行)时,多表JOIN会明显变慢,别在生产环境硬扛

NULL值和边界行的处理最容易被忽略

移动平均遇到开头几行或NULL值时,默认行为未必符合预期:有些数据库对含NULL的窗口仍参与计数(导致分母偏大),有些则跳过NULL但不调整分母。结果可能偏小或报错。

  • 显式过滤NULL:WHERE value IS NOT NULL再开窗,最稳妥
  • IGNORE NULLS(PostgreSQL 15+/BigQuery支持)让窗口自动跳过NULL,但注意不是所有数据库都支持该修饰符
  • 首行的移动平均默认只有自己,第二行是前两行均值……这个“自然收缩”是正常行为,不必强行补0,否则扭曲趋势
  • 如果业务要求固定窗口长度(比如必须3个数,不足就返回NULL),得加CASE WHEN ROW_NUMBER() OVER (...)

移动平均的核心是序列意识——它天然依赖顺序和邻近性。GROUP BY只是给这个序列打标签,不是替代排序。没排好序就加PARTITION BY,等于在乱序数据上强行滑动,结果连调试都难定位。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

3663

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

771

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

949

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5421

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2443

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5420

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7021

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

950

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

832

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133万人学习