聚合与分组
约 1020 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › MySQL › DQL 与查询基础(在新窗口打开) › 聚合与分组
聚合函数(Aggregate Function)对一组值做计算并返回单个值,常配合 GROUP BY 对数据进行分组统计。点击下方运行框试试聚合函数的效果:
▶ SQL 在线运行 加载中...
CREATE TABLE employee (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept VARCHAR(20),
salary DECIMAL(10, 2),
age INT,
join_date DATE
);
INSERT INTO employee (name, dept, salary, age, join_date) VALUES
('张三', '技术部', 15000, 28, '2023-06-01'),
('李四', '市场部', 12000, 35, '2021-03-15'),
('王五', '技术部', 18000, 32, '2022-01-10'),
('赵六', '技术部', 22000, 40, '2020-09-20'),
('孙七', '市场部', 11000, 26, '2024-02-01'),
('周八', '人事部', 9000, 30, '2022-07-11'),
('吴九', '市场部', 13000, 29, '2023-11-05');
聚合函数
| 函数 | 作用 | 对 NULL 的处理 |
|---|---|---|
COUNT() | 计数 | 不计 NULL 行 |
SUM() | 求和 | 忽略 NULL |
AVG() | 平均值 | 忽略 NULL |
MAX() | 最大值 | 忽略 NULL |
MIN() | 最小值 | 忽略 NULL |
-- 员工总数
SELECT COUNT(*) FROM employee; -- 7
-- 统计非空年龄数(与 COUNT(*) 不同)
SELECT COUNT(age) FROM employee; -- 7(若 age 有 NULL 则不计)
-- 薪资总和与平均值
SELECT SUM(salary), AVG(salary) FROM employee;
-- 最高最低薪资
SELECT MAX(salary), MIN(salary) FROM employee;
COUNT(*)统计行数,包括 NULL 行;COUNT(列名)只统计该列非 NULL 的行。
GROUP BY 分组
将数据按一个或多个列分组,再对每组应用聚合函数:
-- 各部门人数
SELECT dept, COUNT(*) AS 人数
FROM employee
GROUP BY dept;
-- 技术部 3, 市场部 3, 人事部 1
-- 各部门平均薪资
SELECT dept, AVG(salary) AS avg_salary
FROM employee
GROUP BY dept;
-- 各部门最高最低年龄
SELECT dept, MIN(age) AS 最小年龄, MAX(age) AS 最大年龄
FROM employee
GROUP BY dept;多列分组
-- 按部门 + 年龄段(以 10 为步长)统计人数
SELECT
dept,
FLOOR(age / 10) * 10 AS age_group,
COUNT(*) AS cnt
FROM employee
GROUP BY dept, age_group
ORDER BY dept, age_group;HAVING:过滤分组后的结果
WHERE 过滤行,HAVING 过滤分组:
-- 平均薪资超过 12000 的部门
SELECT dept, AVG(salary) AS avg_salary
FROM employee
GROUP BY dept
HAVING avg_salary > 12000;
-- 技术部 18333, 市场部 12000(不满足)
-- 人数 >= 2 的部门
SELECT dept, COUNT(*) AS cnt
FROM employee
GROUP BY dept
HAVING cnt >= 2;
-- 技术部 3, 市场部 3WHERE vs HAVING 的区别
-- 先过滤(WHERE 先执行),再分组
SELECT dept, AVG(salary) AS avg_salary
FROM employee
WHERE age > 25 -- 先筛掉年龄 <= 25 的
GROUP BY dept
HAVING avg_salary > 13000; -- 再筛掉平均薪资 <= 13000 的分组执行顺序:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
聚合函数中的 DISTINCT
-- 不重复部门数
SELECT COUNT(DISTINCT dept) FROM employee;
-- 各部门不重复年龄数
SELECT dept, COUNT(DISTINCT age) FROM employee GROUP BY dept;GROUP_CONCAT
将分组中的值拼接为字符串:
-- 列出各部门的员工姓名
SELECT dept, GROUP_CONCAT(name ORDER BY id SEPARATOR ', ') AS 员工列表
FROM employee
GROUP BY dept;
-- 技术部: 张三, 王五, 赵六
-- 市场部: 李四, 孙七, 吴九NULL 与聚合的注意事项
-- 假设 salary 有一行 NULL
SELECT AVG(salary) FROM employee; -- 忽略 NULL
SELECT SUM(salary) / COUNT(*) FROM employee; -- 包含 NULL 分母,结果可能不同SUM + COUNT 手动计算平均值与 AVG 在有 NULL 时不等价:
-- 示例
-- salary: 10000, 20000, NULL
SELECT AVG(salary) FROM t; -- 15000(忽略 NULL 行)
SELECT SUM(salary)/COUNT(*) FROM t; -- 30000/3 = 10000(包含 NULL 行)分组实战:报表查询
-- 各部门薪资统计
SELECT
dept,
COUNT(*) AS 人数,
SUM(salary) AS 总薪资,
AVG(salary) AS 平均薪资,
MIN(salary) AS 最低薪资,
MAX(salary) AS 最高薪资
FROM employee
WHERE join_date >= '2022-01-01' -- 只统计 2022 年后入职的
GROUP BY dept
HAVING 人数 >= 1
ORDER BY 平均薪资 DESC;小结
- 聚合函数(
COUNT/SUM/AVG/MAX/MIN)忽略 NULL,COUNT(*)除外。 GROUP BY将数据按列分组后聚合;可多列分组。WHERE过滤行,HAVING过滤分组——HAVING可以使用聚合函数别名。- SQL 执行顺序:
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。 GROUP_CONCAT可将组内值拼接为字符串,适合做简单列表展示。
上一节:SELECT 基础与条件过滤 下一节:多表关联(JOIN)
