MySQL: MySQL聚合函数与GROUP BY分组详解

最后更新:2026-08-26

聚合函数是数据分析的核心——统计、分组、汇总全靠它们。

本课深入讲解聚合函数和分组查询。

100%
graph TB
    A[聚合查询流程] --> B[FROM 数据源]
    B --> C[WHERE 过滤行]
    C --> D[GROUP BY 分组]
    D --> E[聚合函数<br/>COUNT/SUM/AVG/MAX/MIN]
    E --> F[HAVING 过滤组]
    F --> G[SELECT 输出]
    G --> H[ORDER BY 排序]
    H --> I[WITH ROLLUP 汇总]

1. 你将学到


2. 报表统计的真实故事

(1) 痛点:Excel 统计太慢

一个运营要统计:每个部门的员工数、平均工资、最高工资。

用 Excel 需要写多个公式,数据更新还要重算。

(2) 聚合函数的解法

SQL
SELECT 
    department,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary
FROM employees
GROUP BY department;

3. 聚合函数

(1) COUNT 计数

SQL
-- 统计总行数
SELECT COUNT(*) FROM employees;

-- 统计非 NULL 的行数
SELECT COUNT(email) FROM employees;

-- 统计去重数量
SELECT COUNT(DISTINCT department) FROM employees;
⚠️ 注意: COUNT(*) 统计所有行,COUNT(col) 统计 col 非 NULL 的行。

(2) SUM/AVG 求和/平均

SQL
-- 总工资
SELECT SUM(salary) FROM employees;

-- 平均工资
SELECT AVG(salary) FROM employees;

-- 条件求和
SELECT SUM(amount) FROM orders WHERE status = 'paid';

(3) MAX/MIN 最大/最小

SQL
-- 最高工资
SELECT MAX(salary) FROM employees;

-- 最低工资
SELECT MIN(salary) FROM employees;

-- 最早/最晚日期
SELECT MIN(hire_date) AS earliest, MAX(hire_date) AS latest FROM employees;

4. GROUP BY 分组

▶ 示例:单字段分组

SQL
-- 按部门统计
SELECT 
    department,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department;
▶ 试一试

输出:

TEXT 📖 仅展示
+-------------+-----------+-----------+
| department  | emp_count | avg_salary|
+-------------+-----------+-----------+
| Engineering |        15 |   8500.00 |
| Sales       |        10 |   6000.00 |
| Marketing   |         8 |   5500.00 |
| HR          |         5 |   5000.00 |
+-------------+-----------+-----------+

▶ 示例:多字段分组

SQL
-- 按部门+职位统计
SELECT 
    department,
    job_title,
    COUNT(*) AS count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department, job_title
ORDER BY department, avg_salary DESC;
▶ 试一试

▶ 示例:按表达式分组

SQL
-- 按年份统计订单
SELECT 
    YEAR(order_date) AS year,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount
FROM orders
GROUP BY YEAR(order_date);

-- 按年龄段统计
SELECT 
    CASE 
        WHEN age < 18 THEN 'Under 18'
        WHEN age BETWEEN 18 AND 30 THEN '18-30'
        WHEN age BETWEEN 31 AND 50 THEN '31-50'
        ELSE 'Over 50'
    END AS age_group,
    COUNT(*) AS count
FROM users
GROUP BY age_group;
▶ 试一试

5. HAVING 过滤分组

WHERE 过滤行,HAVING 过滤组。

▶ 示例:HAVING 用法

SQL
-- 找出平均工资 > 6000 的部门
SELECT 
    department,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING avg_salary > 6000;

-- 找出订单数 > 10 的客户
SELECT 
    customer_id,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING order_count > 10;

-- WHERE + GROUP BY + HAVING 组合
SELECT 
    department,
    AVG(salary) AS avg_salary
FROM employees
WHERE hire_date >= '2024-01-01'  -- 先过滤行
GROUP BY department               -- 再分组
HAVING avg_salary > 6000;        -- 最后过滤组
▶ 试一试

6. WITH ROLLUP 汇总

▶ 示例:ROLLUP 汇总行

SQL
-- 自动生成总计行
SELECT 
    department,
    COUNT(*) AS emp_count,
    SUM(salary) AS total_salary
FROM employees
GROUP BY department WITH ROLLUP;
▶ 试一试

输出:

TEXT 📖 仅展示
+-------------+-----------+--------------+
| department  | emp_count | total_salary |
+-------------+-----------+--------------+
| Engineering |        15 |    127500.00 |
| Sales       |        10 |     60000.00 |
| Marketing   |         8 |     44000.00 |
| HR          |         5 |     25000.00 |
| NULL        |        38 |    256500.00 |  -- 总计
+-------------+-----------+--------------+

7. 聚合函数与 NULL

函数 NULL 处理
COUNT(*) 包含 NULL 行
COUNT(col) 排除 NULL
SUM(col) 忽略 NULL
AVG(col) 忽略 NULL(分母不含 NULL)
MAX(col) 忽略 NULL
MIN(col) 忽略 NULL

▶ 示例:NULL 处理

SQL
-- COUNT(*) vs COUNT(email)
SELECT 
    COUNT(*) AS total_rows,
    COUNT(email) AS has_email,
    COUNT(*) - COUNT(email) AS no_email
FROM users;

-- AVG 忽略 NULL
SELECT AVG(salary) FROM employees;  -- NULL 不参与计算
▶ 试一试

❓ 常见问题

Q WHERE 和 HAVING 能一起用吗?
A 可以。执行顺序:WHERE(过滤行)→ GROUP BY(分组)→ HAVING(过滤组)→ SELECT → ORDER BY。
Q GROUP BY 的字段必须在 SELECT 中吗?
A MySQL 严格模式要求 SELECT 的非聚合字段必须在 GROUP BY 中。
Q COUNT(*) 和 COUNT(1) 哪个快?
A 一样快。MySQL 优化器会自动选择最优方式。
Q 聚合函数能嵌套吗?
A 不能直接嵌套 AVG(SUM(...))。用子查询:SELECT AVG(total) FROM (SELECT SUM(amount) AS total FROM orders GROUP BY customer_id) t

📖 小节


📝 作业

  1. 基础题(难度⭐):统计每个部门的员工数量和平均工资。

  2. 进阶题(难度⭐⭐):找出总订单金额 > 10000 的客户,按总金额降序排列。

  3. 挑战题(难度⭐⭐⭐):统计每个月的订单数量、总金额和平均金额,使用 WITH ROLLUP 添加年度汇总行。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏