数据汇总与分组
AVG/COUNT/MAX/MIN/SUM 聚合、GROUP BY 分组、HAVING 过滤分组
聚合函数:一眼看全貌
前面都是逐行看数据,但老板要的往往是"公司平均工资多少?总共多少人?最高工资是谁?"——这是聚合函数的活。
AVG:平均值
SELECT AVG(salary) FROM users;+--------------+
| AVG(salary) |
+--------------+
| 9875.000000 |
+--------------+公司平均工资 9875。
COUNT:计数
SELECT COUNT(*) FROM users;+----------+
| COUNT(*) |
+----------+
| 8 |
+----------+8 个人。
COUNT 有两种用法,结果可能不一样:
-- 统计所有行(包括 NULL)
SELECT COUNT(*) FROM users;
-- 统计某列非 NULL 的行数
SELECT COUNT(age) FROM users;COUNT(*) 数所有行,COUNT(列名) 忽略 NULL。如果 age 列有 NULL,两者结果就不同了。
MAX 和 MIN
SELECT MAX(salary) FROM users; -- 15000.00
SELECT MIN(salary) FROM users; -- 6000.00SUM:求和
SELECT SUM(salary) FROM users; -- 79000.00公司月工资总支出 79000。
聚合去重
想算"公司有多少个不同城市",可以在聚合函数里加 DISTINCT:
SELECT COUNT(DISTINCT city) FROM users; -- 58 个人分布在 5 个城市。
SELECT AVG(DISTINCT salary) FROM users;⚠️
AVG(DISTINCT ...)很少用——去重后的平均值往往不是你想要的那个"平均"。
GROUP BY:分组统计
聚合函数给的是全表一个值,但通常要的是分组的统计——"每个城市的平均工资"。
基本用法
SELECT city, AVG(salary) AS avg_salary
FROM users
GROUP BY city;+--------+------------+
| city | avg_salary |
+--------+------------+
| 北京 | 7266.6667 |
| 上海 | 11500.0000 |
| 广州 | 9500.0000 |
| 深圳 | 15000.0000 |
| 杭州 | 13000.0000 |
+--------+------------+GROUP BY city 的意思是——把相同 city 的行归为一组,然后对每组分别算 AVG(salary)。
多列分组
按城市 + 年龄分组(每个城市每个年龄段的平均工资):
SELECT city, age, AVG(salary) AS avg_salary
FROM users
GROUP BY city, age;GROUP BY 的规则
📝 一条铁律:SELECT 里除了聚合函数之外的列,必须出现在 GROUP BY 里。
-- ❌ 错误:name 不在 GROUP BY 中,MySQL 不知道拿哪个 name
SELECT name, city, AVG(salary) FROM users GROUP BY city;
-- ✅ 正确
SELECT city, AVG(salary) FROM users GROUP BY city;HAVING:过滤分组
WHERE 是分组前过滤行,HAVING 是分组后过滤分组。
想查"平均工资大于 10000 的城市":
SELECT city, AVG(salary) AS avg_salary
FROM users
GROUP BY city
HAVING AVG(salary) > 10000;+--------+------------+
| city | avg_salary |
+--------+------------+
| 上海 | 11500.0000 |
| 深圳 | 15000.0000 |
| 杭州 | 13000.0000 |
+--------+------------+WHERE 和 HAVING 能同时用
"先排除孙七(工资太低干扰),再算各城市平均,看看哪个城市平均超 10000":
SELECT city, AVG(salary) AS avg_salary
FROM users
WHERE name <> '孙七'
GROUP BY city
HAVING AVG(salary) > 10000;流程:先 WHERE 筛掉孙七 → 再 GROUP BY 分组 → 最后 HAVING 过滤分组。
WHERE vs HAVING
| WHERE | HAVING | |
|---|---|---|
| 作用对象 | 行 | 分组 |
| 执行时机 | 分组前 | 分组后 |
| 能用聚合函数吗 | ❌ | ✅ |
💡 简单的记法:行级别过滤用 WHERE,组级别过滤用 HAVING。
SELECT 子句执行顺序
写 SQL 的顺序和 MySQL 执行的顺序不一样:
你写的顺序 → SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
实际执行顺序 → FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
理解这个顺序很重要。比如 SELECT 里定义的别名,WHERE 里不能用(因为 WHERE 先执行),但 ORDER BY 里可以用:
-- ❌ WHERE 里不能用别名
SELECT city, AVG(salary) AS avg_salary
FROM users
WHERE avg_salary > 10000 -- 报错!WHERE 执行时别名还没诞生
GROUP BY city;
-- ✅ ORDER BY 里可以用
SELECT city, AVG(salary) AS avg_salary
FROM users
GROUP BY city
ORDER BY avg_salary DESC; -- 没问题小结
从"看每一行"到"看整体面貌",聚合和分组是数据分析的基础:
- 五大聚合函数 —
AVGCOUNTMAXMINSUM,一眼看全局 - COUNT(*) vs COUNT(列) — 前者数所有行,后者只数非 NULL 值
- GROUP BY 分组 — 按城市统计、按部门统计,聚合 + 分组才是完整形态
- HAVING 过滤分组 — WHERE 过滤行,HAVING 过滤分组,别搞混
- 执行顺序刻在脑子里 —
WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
自主练习
- 计算公司员工的平均年龄
- 统计每个城市的员工人数和平均工资
- 找出平均工资大于 8000 的城市
- 排除年龄小于 25 的人后,统计每个城市的最高工资
- 按工资总和降序排列各城市