在SQL的日常使用中,我们经常会遇到需要按某个维度统计数据的情况,比如统计每个部门的员工人数、计算每个类别的商品总销量等,这时候就需要用到GROUP BY语句。GROUP BY是SQL中用于数据分组的核心语句,能够将具有相同特征的行归为一组,再结合聚合函数完成分组后的统计计算,是数据分析类SQL查询的必备技能。

GROUP BY的基础语法与作用
GROUP BY的核心作用是对查询结果集按照一个或多个列的值进行分组,分组后相同列值的行会被合并为一组,每组只会返回一条汇总后的记录。它的基础语法结构如下:
SELECT 分组列, 聚合函数(统计列) FROM 表名 WHERE 筛选条件 GROUP BY 分组列 ORDER BY 排序列;
需要注意几个执行顺序的规则:首先执行FROM子句找到目标表,然后执行WHERE子句过滤符合条件的行,接着执行GROUP BY对过滤后的数据进行分组,之后对每个分组执行SELECT后的聚合函数计算,最后执行ORDER BY对结果排序。这个执行顺序非常重要,会直接影响查询的逻辑和结果。
搭配聚合函数的使用方式
GROUP BY单独使用通常没有实际意义,必须配合聚合函数才能发挥分组统计的作用。常用的聚合函数有以下几种:
- COUNT():统计每组内的行数,COUNT(*)统计所有行,COUNT(列名)统计该列非空值的行数
- SUM():计算每组内数值列的总和
- AVG():计算每组内数值列的平均值
- MAX():返回每组内指定列的最大值
- MIN():返回每组内指定列的最小值
我们通过一个员工表的例子来演示用法,假设我们有如下员工表employee:
| emp_id | emp_name | dept_id | salary | hire_date |
|---|---|---|---|---|
| 1001 | 张三 | 10 | 8000 | 2020-03-15 |
| 1002 | 李四 | 10 | 9000 | 2021-06-20 |
| 1003 | 王五 | 20 | 7500 | 2019-11-08 |
| 1004 | 赵六 | 20 | 8500 | 2022-01-12 |
| 1005 | 孙七 | 30 | 7000 | 2020-08-25 |
如果我们要统计每个部门的员工人数,查询语句如下:
SELECT dept_id, COUNT(*) AS emp_count FROM employee GROUP BY dept_id;
执行后结果会是:部门10有2人,部门20有2人,部门30有1人。如果要计算每个部门的平均工资,语句可以写成:
SELECT dept_id, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id;
多列分组的用法
GROUP BY不仅支持单列分组,还支持按照多个列进行分组,多列分组时会先按照第一个列分组,在第一个列值相同的组内再按照第二个列分组,以此类推。比如我们要统计每个部门中不同入职年份的员工人数,可以先提取入职日期的年份,再按部门和年份分组:
SELECT
dept_id,
YEAR(hire_date) AS hire_year,
COUNT(*) AS emp_count
FROM employee
GROUP BY dept_id, YEAR(hire_date)
ORDER BY dept_id, hire_year;这时候分组维度就是部门和入职年份的组合,只有当两个员工的部门相同且入职年份也相同时,才会被分到同一组。
WHERE和HAVING的区别
很多用户会混淆WHERE和HAVING的用法,两者的核心区别是作用阶段不同:WHERE是在分组前对行数据进行筛选,不符合条件的行不会进入分组阶段;HAVING是在分组后对分组结果进行筛选,只能使用分组列和聚合函数作为筛选条件。
比如我们要统计员工人数大于1的部门,就需要用HAVING:
SELECT dept_id, COUNT(*) AS emp_count FROM employee GROUP BY dept_id HAVING COUNT(*) > 1;
如果我们只想统计工资大于7500的员工,再按部门分组统计人数,就需要用WHERE先过滤:
SELECT dept_id, COUNT(*) AS emp_count FROM employee WHERE salary > 7500 GROUP BY dept_id;
这里要注意,WHERE子句中不能使用聚合函数,比如不能写WHERE COUNT(*) > 1,这种写法会直接报错,聚合函数的筛选必须放在HAVING中。
SELECT子句的注意事项
使用GROUP BY时,SELECT子句中出现的列必须满足两个条件之一:要么是GROUP BY后面指定的分组列,要么是被聚合函数包裹的列。如果SELECT中出现了既不是分组列也没有被聚合函数包裹的列,查询的结果是不确定的,不同数据库的实现可能返回不同的值,标准SQL中这种写法是不允许的。
比如下面的语句就是错误的:
-- 错误示例,emp_name既不是分组列也没有被聚合 SELECT dept_id, emp_name, COUNT(*) AS emp_count FROM employee GROUP BY dept_id;
因为同一个部门有多个员工,emp_name的值不唯一,数据库不知道应该返回哪个员工的名字,所以这种写法要避免。
常见使用场景与案例
场景1:业务数据统计
比如我们有一个订单表orders,字段包括order_id、user_id、order_amount、order_date,要统计每个用户的订单总金额和订单数量,语句如下:
SELECT
user_id,
COUNT(order_id) AS order_count,
SUM(order_amount) AS total_amount
FROM orders
GROUP BY user_id
ORDER BY total_amount DESC;场景2:数据去重
如果要查询所有有订单的用户ID,也可以用GROUP BY实现去重,效果和DISTINCT类似:
SELECT user_id FROM orders GROUP BY user_id;
不过如果只需要去重,DISTINCT的性能通常会更好,GROUP BY更适合需要同时做统计的场景。
场景3:分组后取每组最大值记录
比如要查询每个部门工资最高的员工信息,可以结合子查询实现:
SELECT e.*
FROM employee e
JOIN (
SELECT dept_id, MAX(salary) AS max_salary
FROM employee
GROUP BY dept_id
) t ON e.dept_id = t.dept_id AND e.salary = t.max_salary;使用GROUP BY的常见错误
- 在WHERE子句中使用聚合函数,正确做法是将聚合函数筛选放到HAVING中
- SELECT子句中出现未分组也未聚合的列,导致结果异常
- 忘记GROUP BY的执行顺序,在WHERE中使用分组后的计算结果,比如WHERE AVG(salary) > 8000,这种写法会报错
- 多列分组时顺序搞反,导致分组维度和预期不符
只要掌握GROUP BY的基础语法、执行顺序,以及和聚合函数、WHERE、HAVING的搭配规则,就能应对大部分基础的数据分组统计需求,在实际业务中灵活使用可以大幅提升数据查询的效率。