用 GROUP BY 对数据分组
第 4.2 课 · 阅读时间:约 7 分钟 · 练习题 ↓
分组把单独的行转换为按类别划分的指标。当报表需要按类别、日期或其他维度计算合计时,GROUP BY 必不可少。在本课中,你将学习 GROUP BY 的语法、核心规则以及基于 Sakila 的实用示例。学完后,你将能够熟练编写带分组的分析查询。
在上一课中,你学习了聚合函数。现在进入下一步:把这些函数应用到不同的数据类别上。
GROUP BY 的做法是把数据划分为若干组,并分别为每一组计算指标。
GROUP BY 语法
基本结构:
SELECT column1, AGG_FUNCTION(column2)
FROM table
GROUP BY column1;
核心规则
使用 GROUP BY 时,SELECT 中的每一列都必须:
- 要么出现在
GROUP BY列表中; - 要么被包在聚合函数(
SUM、COUNT、AVG、MIN、MAX)中。
这条规则可以避免歧义:当一个组包含多行时,SQL 需要知道该返回哪个值。
按一列分组
每位客户的付款总额
SELECT customer_id, SUM(amount) AS total_paid
FROM payment
GROUP BY customer_id;
结果:每位客户一行,包含其付款总额。
每位员工处理的付款笔数
SELECT staff_id, COUNT(*) AS payments_count
FROM payment
GROUP BY staff_id;
结果:每位员工处理的付款笔数。
按日期计算平均付款额
SELECT DATE(payment_date) AS pay_date, AVG(amount) AS avg_payment
FROM payment
GROUP BY DATE(payment_date);
结果:每个日期的平均付款金额。
变体:在 GROUP BY 中使用别名
SELECT DATE(payment_date) AS pay_date, AVG(amount) AS avg_payment
FROM payment
GROUP BY pay_date;
说明:这在 MySQL/MariaDB 中可行,但并非所有 DBMS 都支持。为了跨数据库兼容,请完整写出 GROUP BY DATE(payment_date)。
按多列分组
你可以同时按多个字段分组,进行更细致的分析。
按员工和客户统计付款总额
SELECT staff_id, customer_id, SUM(amount) AS total_paid
FROM payment
GROUP BY staff_id, customer_id;
结果:每个“员工-客户”组合一行,包含其付款总额。
实用示例
每日收入报表
SELECT DATE(payment_date) AS pay_date, SUM(amount) AS total_sales
FROM payment
GROUP BY DATE(payment_date)
ORDER BY pay_date;
按租赁次数排列最活跃的客户
SELECT customer_id, COUNT(*) AS rentals_count
FROM rental
GROUP BY customer_id
ORDER BY rentals_count DESC;
按电影类别计算平均租金
SELECT category_id, AVG(rental_rate) AS avg_rental_rate
FROM film f
JOIN film_category fc ON f.film_id = fc.film_id
GROUP BY category_id;
常见问题
为什么使用 GROUP BY 时不能在 SELECT 中随意放列?
因为聚合函数(SUM、COUNT、AVG)已经告诉 SQL 如何处理组内的所有行。如果某一列没有被聚合,它就必须出现在 GROUP BY 中,这样 SQL 才知道该选哪个值。
可以按表达式而不是列分组吗?
可以,例如 GROUP BY DATE(payment_date) 或 GROUP BY YEAR(payment_date)。SELECT 和 GROUP BY 中的表达式必须一致。
如果 GROUP BY 后面是空的会怎样?
这是语法错误。GROUP BY 至少需要一列或一个表达式。
面试问题
什么是 GROUP BY,为什么需要它?
GROUP BY 把所选列取值相同的行合并为一组。然后你可以对每一组应用聚合函数,得到汇总统计。
为什么使用 GROUP BY 时不能随意选择一列?
因为一个组可能包含多行,而这些行在该列上的值各不相同。SQL 需要知道返回哪个值,否则结果就会有歧义。因此该列必须出现在 GROUP BY 中,或者放在聚合函数里。
WHERE 和 HAVING 有什么区别?
WHERE 在分组之前过滤行,而 HAVING 在分组之后过滤组。例如,WHERE amount > 10 在分组前排除行,而 HAVING SUM(amount) > 100 排除总和小于 100 的组。
本课要点:
GROUP BY把数据分成若干组,并对每一组应用聚合函数。- 核心规则:SELECT 中的每一列要么在 GROUP BY 中,要么在聚合函数中。
- 可以同时按一列或多列分组。
- 可以按表达式分组,例如
GROUP BY DATE(payment_date)。 GROUP BY是报表、分析和分类汇总的基础。