以下是如何使用 SQL 中的 GROUP BY
和 HAVING
子句进行分组和筛选的完整指南,结合核心概念、语法规则、实际示例及注意事项:
一、核心概念与功能
GROUP BY
的作用- 根据指定列的值将数据行分组,通常与聚合函数(如
SUM
、COUNT
、AVG
、MAX
、MIN
)结合使用,对每组数据计算汇总结果。 - 应用场景:统计每个部门的平均工资、计算每个产品的总销售额、按地区分组用户数量等 。
- 关键规则:
- 根据指定列的值将数据行分组,通常与聚合函数(如
SELECT
子句中只能包含 分组列 或 聚合函数,不能直接包含非分组列 。- 支持单列或多列分组(如
GROUP BY department, location
)。
HAVING
的作用- 在
GROUP BY
分组后,对聚合结果进行筛选(例如:仅保留总销售额 > $1000 的分组)。 - 与
WHERE
的本质区别:
- 在
WHERE
在分组前过滤原始数据行,不能使用聚合函数(如WHERE salary > 5000
)。HAVING
在分组后过滤聚合结果,必须使用聚合函数或分组列(如HAVING AVG(salary) > 10000
)。
二、语法结构与执行顺序
SELECT 分组列, 聚合函数(列)
FROM 表名
WHERE 行级过滤条件 -- 可选,分组前过滤
GROUP BY 分组列
HAVING 分组过滤条件 -- 基于聚合结果筛选
ORDER BY 排序列; -- 可选
执行顺序:
FROM
→ WHERE
→ GROUP BY
→ 聚合计算 → HAVING
→ SELECT
→ ORDER BY
。
三、典型使用示例
场景 1:统计每个部门的平均工资,仅显示 > $10,000 的部门
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 10000; -- 对聚合结果筛选
解析:
- 先按
department
分组,计算每组平均工资。HAVING
筛选出平均工资 > $10,000 的组 。
场景 2:找出总消费金额 > $180 的客户
SELECT customer_id, SUM(amount) AS total
FROM payment
GROUP BY customer_id
HAVING SUM(amount) > 180 -- 使用聚合函数结果筛选
ORDER BY total DESC;
结果示例:
customer_id total 526 221.55 148 216.54
场景 3:筛选订单数 ≥ 5 的客户
SELECT customer_id, COUNT(order_id) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(order_id) >= 5; -- 直接使用聚合函数
四、常见错误与注意事项
1.SELECT
列限制
-- 错误示例(name 未在 GROUP BY 中):
SELECT name, AVG(salary) FROM employees GROUP BY department;
正确做法:SELECT
中只能包含分组列(department
)或聚合函数 。
2.HAVING
条件无效
-- 错误:HAVING 不能直接引用非聚合列(如 salary)
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING salary > 5000; -- 应改用 WHERE 过滤行
修正:
- 行级条件(如
salary > 5000
)用WHERE
; - 分组后条件(如
AVG(salary) > 10000
)用HAVING
。
3.性能优化
-
- 在大型数据集上,优先用
WHERE
减少分组前的数据量,再用HAVING
二次过滤 。 - 为分组列和聚合条件列建立索引可加速查询 。
- 在大型数据集上,优先用
五、进阶用法
1.多列分组
-- 按部门和职位统计人数,保留人数 > 10 的组
SELECT department, job_title, COUNT(*) AS emp_count
FROM employees
GROUP BY department, job_title
HAVING COUNT(*) > 10;
2.结合 ROLLUP
/CUBE
(生成层次化汇总)
-- 按部门和年份汇总销售额,并生成小计和总计
SELECT department, YEAR(order_date), SUM(sales)
FROM orders
GROUP BY ROLLUP(department, YEAR(order_date));
-
结果:除分组结果外,额外返回部门小计和总计行 。
六、总结对比表
子句 | 执行时机 | 可用的条件类型 | 典型用途 |
---|---|---|---|
WHERE | 分组前 | 列值比较(禁用聚合函数) | 过滤原始数据行 |
HAVING | 分组后 | 聚合函数或分组列 | 筛选分组结果 |
GROUP BY | 在 WHERE 后 | 分组列定义 | 创建数据组以便聚合计算 |
核心原则:
- 行级过滤 →
WHERE
;- 分组操作 →
GROUP BY
;- 分组后过滤 →
HAVING
。
通过以上详解和示例,可系统掌握 GROUP BY
和 HAVING
的分组筛选逻辑,避免常见错误,高效完成数据聚合分析任务。