Skip to content

数据汇总与分组

AVG/COUNT/MAX/MIN/SUM 聚合、GROUP BY 分组、HAVING 过滤分组

聚合函数:一眼看全貌

前面都是逐行看数据,但老板要的往往是"公司平均工资多少?总共多少人?最高工资是谁?"——这是聚合函数的活。

AVG:平均值

sql
SELECT AVG(salary) FROM users;
+--------------+
| AVG(salary)  |
+--------------+
|  9875.000000 |
+--------------+

公司平均工资 9875。

COUNT:计数

sql
SELECT COUNT(*) FROM users;
+----------+
| COUNT(*) |
+----------+
|        8 |
+----------+

8 个人。

COUNT 有两种用法,结果可能不一样:

sql
-- 统计所有行(包括 NULL)
SELECT COUNT(*) FROM users;

-- 统计某列非 NULL 的行数
SELECT COUNT(age) FROM users;

COUNT(*) 数所有行,COUNT(列名) 忽略 NULL。如果 age 列有 NULL,两者结果就不同了。

MAX 和 MIN

sql
SELECT MAX(salary) FROM users;   -- 15000.00
SELECT MIN(salary) FROM users;   --  6000.00

SUM:求和

sql
SELECT SUM(salary) FROM users;   -- 79000.00

公司月工资总支出 79000。

聚合去重

想算"公司有多少个不同城市",可以在聚合函数里加 DISTINCT

sql
SELECT COUNT(DISTINCT city) FROM users;   -- 5

8 个人分布在 5 个城市。

sql
SELECT AVG(DISTINCT salary) FROM users;

⚠️ AVG(DISTINCT ...) 很少用——去重后的平均值往往不是你想要的那个"平均"。

GROUP BY:分组统计

聚合函数给的是全表一个值,但通常要的是分组的统计——"每个城市的平均工资"。

基本用法

sql
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)

多列分组

按城市 + 年龄分组(每个城市每个年龄段的平均工资):

sql
SELECT city, age, AVG(salary) AS avg_salary
FROM users
GROUP BY city, age;

GROUP BY 的规则

📝 一条铁律:SELECT 里除了聚合函数之外的列,必须出现在 GROUP BY 里

sql
-- ❌ 错误: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 的城市":

sql
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":

sql
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

WHEREHAVING
作用对象分组
执行时机分组前分组后
能用聚合函数吗

💡 简单的记法:行级别过滤用 WHERE,组级别过滤用 HAVING。

SELECT 子句执行顺序

写 SQL 的顺序和 MySQL 执行的顺序不一样

你写的顺序 → SELECTFROMWHEREGROUP BYHAVINGORDER BYLIMIT

实际执行顺序 → FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT

理解这个顺序很重要。比如 SELECT 里定义的别名,WHERE 里不能用(因为 WHERE 先执行),但 ORDER BY 里可以用:

sql
-- ❌ 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;   -- 没问题

小结

从"看每一行"到"看整体面貌",聚合和分组是数据分析的基础:

  1. 五大聚合函数AVG COUNT MAX MIN SUM,一眼看全局
  2. COUNT(*) vs COUNT(列) — 前者数所有行,后者只数非 NULL 值
  3. GROUP BY 分组 — 按城市统计、按部门统计,聚合 + 分组才是完整形态
  4. HAVING 过滤分组 — WHERE 过滤行,HAVING 过滤分组,别搞混
  5. 执行顺序刻在脑子里WHERE → GROUP BY → HAVING → ORDER BY → LIMIT

自主练习

  1. 计算公司员工的平均年龄
  2. 统计每个城市的员工人数和平均工资
  3. 找出平均工资大于 8000 的城市
  4. 排除年龄小于 25 的人后,统计每个城市的最高工资
  5. 按工资总和降序排列各城市

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8