从入门到高阶实战详解如何使用MySQL做数据统计
摘要:数据统计是后端开发和数据分析的高频需求。是用代码在内存里算,还是让数据库算?本文深入讲解 MySQL 统计分析的核心能力,涵盖聚合函数、分组汇总、去重计数、窗口函数以及性能优化策略,帮你搞定 90% 的数据统计场景。
一、 前言:别把计算压力扔给应用层
很多初级开发在做统计时,习惯把数据全查出来,在 Java/Python/Go 代码里循环计算:
# ❌ 错误示范:把 100 万行数据拉到内存里算
rows = db.query("SELECT * FROM orders WHERE status='paid'")
total = 0
for row in rows:
total += row.amount
这种做法不仅慢,还会把应用服务器的内存打爆。MySQL 内置了强大的统计函数,让数据在磁盘端就完成计算,只返回结果集,这才是正确姿势。
二、 基础篇:核心聚合函数
这是统计的基石,必须熟练掌握。
1. COUNT 系列:数数的艺术
COUNT(*):统计行数(不关心 NULL,性能最优)。COUNT(field):统计指定字段非 NULL 的行数。COUNT(DISTINCT field):统计去重后的数量。
坑点:SELECT COUNT(*) FROM table 在 InnoDB 中其实很快(不像 MyISAM 那样存了元数据,但 InnoDB 会走最小的二级索引),千万别为了加速而用 COUNT(1),优化器会自动把它们变成一样的执行计划。
2. SUM / AVG / MAX / MIN
最基础的求和与极值。
-- 统计近 7 天的总销售额和平均客单价
SELECT
SUM(amount) as total_sales,
AVG(amount) as avg_price,
MAX(create_time) as last_order_time
FROM orders
WHERE create_time > NOW() - INTERVAL 7 DAY;
三、 进阶篇:分组与多维分析
单看总数没意义,我们需要按维度拆解。
1. GROUP BY:按维度切分
-- 按城市统计用户数 SELECT city, COUNT(*) FROM users GROUP BY city;
优化技巧:GROUP BY 的字段必须建立索引,否则会产生临时表(Using temporary)和文件排序(Using filesort),性能极差。
2. WITH ROLLUP:小计与总计
如果你需要“各城市小计 + 全国总计”,不需要写两条 SQL,用 WITH ROLLUP 一把梭:
SELECT city, COUNT(*) FROM users GROUP BY city WITH ROLLUP;
结果会多一行 NULL,这就是总计。
3. GROUPING SETS:多维度交叉
假设你要同时看“按性别统计”和“按年龄统计”,传统写法要 UNION ALL,现在可以用:
SELECT gender, age_group, COUNT(*) FROM users GROUP BY GROUPING SETS ( (gender), (age_group) );
四、 高阶篇:窗口函数(MySQL 8.0+)
这是现代 SQL 的大杀器,能解决“排名”、“累计”、“移动平均”等难题,无需自连接。
1. 排名问题:谁是 Top N?
-- 查询每个部门工资前 3 名的员工(允许并列)
SELECT name, department, salary,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) as ranking
FROM employees;
ROW_NUMBER():不重复排名 (1, 2, 3)RANK():跳跃排名 (1, 1, 3)DENSE_RANK():连续排名 (1, 1, 2)
2. 累计与移动平均
-- 计算每日销售额及近 3 天移动平均(MA3)
SELECT date, sales,
AVG(sales) OVER (
ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) as moving_avg_3d
FROM daily_report;
3. 同比/环比
利用 LAG() 函数取上一行的值:
SELECT date, sales,
LAG(sales, 1) OVER (ORDER BY date) as prev_day_sales,
(sales - LAG(sales, 1) OVER (ORDER BY date)) / LAG(sales, 1) OVER (ORDER BY date) * 100 as growth_rate
FROM daily_report;
五、 实战篇:时间序列统计
做报表时,最怕数据“断档”。比如统计最近 30 天的日活,如果某天没数据,SQL 不会返回 0,而是直接跳过这一天。
解决方案:补全时间轴
MySQL 本身没有生成序列的函数(不像 PostgreSQL 的 generate_series),我们需要用“数字表”或者递归 CTE 来制造一个连续的时间序列,然后左连接业务表。
-- 1. 生成连续日期(递归 CTE,MySQL 8.0+)
WITH RECURSIVE date_series AS (
SELECT CURDATE() - INTERVAL 29 DAY as dt
UNION ALL
SELECT dt + INTERVAL 1 DAY FROM date_series WHERE dt < CURDATE()
)
-- 2. 左连接业务表,用 IFNULL 补 0
SELECT
d.dt,
COUNT(o.id) as order_count, -- 这里统计的是左连接后的非空行
IFNULL(COUNT(o.id), 0) as real_count -- 更严谨的写法其实是 SUM(CASE WHEN o.id IS NOT NULL THEN 1 ELSE 0 END)
FROM date_series d
LEFT JOIN orders o ON DATE(o.create_time) = d.dt
GROUP BY d.dt
ORDER BY d.dt;
注:如果是低版本 MySQL,需要建一张辅助的数字表(Numbers Table)。
六、 性能优化:让统计飞起来
统计查询通常涉及全表扫描或大范围扫描,如何优化?
1.覆盖索引(Covering Index)
如果统计只涉及索引列,MySQL 就不需要回表查数据行。
-- 假设有索引 idx_city (city) SELECT city, COUNT(*) FROM users GROUP BY city; -- 极快,只扫索引
2.避免 SELECT
统计时只查需要的字段,减少网络传输和内存开销。
3.近似计算
如果不需要 100% 精确(如 UV 统计),用 APPROX_DISTINCT(基于 HyperLogLog 算法)比 COUNT(DISTINCT) 快 10 倍以上,且内存占用极小。
SELECT APPROX_DISTINCT(user_id) FROM logs;
4.分表/分库/OLAP
如果单表数据量过亿,统计压力大,考虑分库分表。
如果统计逻辑极其复杂(多表关联、Ad-hoc 查询),不要死磕 MySQL。将数据同步到 ClickHouse、Doris 或 TiDB 这类 OLAP 数据库,查询速度能提升 10-100 倍。
七、 总结:统计函数速查表
| 需求场景 | 推荐函数/语法 | 备注 |
|---|---|---|
| 简单计数/求和 | COUNT, SUM, AVG | 基础中的基础 |
| 分组统计 | GROUP BY | 必须带索引 |
| 多级汇总 | WITH ROLLUP | 替代多次查询 |
| 排名/TopN | RANK(), DENSE_RANK() | 窗口函数 |
| 累计/移动平均 | SUM() OVER (... ROWS BETWEEN) | 窗口函数 |
| 同比/环比 | LAG(), LEAD() | 窗口函数 |
| 近似去重 | APPROX_DISTINCT | 大数据量首选 |
| 时间轴补全 | 递归 CTE + Left Join | 解决数据断层 |
最后的一句话:MySQL 不仅是一个 OLTP(事务处理)数据库,掌握好上述统计技巧,它也能胜任轻量级的 OLAP(分析)工作。但如果你的业务是“双十一大屏实时监控”或者“每天跑几百个复杂报表”,请果断上 ClickHouse。
到此这篇关于从入门到高阶实战详解如何使用MySQL做数据统计的文章就介绍到这了,更多相关MySQL数据统计内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
在IDEA的maven项目中连接并使用MySQL8.0的方法教程
这篇文章主要介绍了如何在IDEA的maven项目中连接并使用MySQL8.0,本文分步骤给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下2020-02-02
MySQL中的聚簇索引、非聚簇索引、联合索引和唯一索引详细介绍
本文主要介绍了MySQL的索引类型,根据索引的存储方式来划分,索引可以分为聚簇索引和非聚簇索引。聚簇索引的特点是叶子节点包含了完整的记录行,而非聚簇索引的叶子节点只有所以字段和主键ID,感兴趣的同学可以阅读本文2023-04-04
使用MySQL的Explain执行计划的方法(SQL性能调优)
这篇文章主要介绍了使用MySQL的Explain执行计划的方法(SQL性能调优),使用EXPLAIN关键字可以模拟优化器执行SQL语句,具体详解,需要的小伙伴可以参考一下2022-08-08


最新评论