基本语法
SELECT 列1, 列2, ..., 聚合函数(列)
FROM 表名
[WHERE 条件]
GROUP BY 分组列
HAVING 分组后条件;
HAVING 与 WHERE 的核心区别
特性 | WHERE 子句 | HAVING 子句 |
---|---|---|
执行时机 | 在分组前过滤数据 | 在分组后过滤数据 |
可使用聚合函数 | 不可以 | 可以 |
可使用字段别名 | 不可以 | 可以 |
操作对象 | 原始表数据 | 分组后的结果集 |
性能影响 | 先过滤可减少分组数据量 | 对已分组数据做二次过滤 |
使用场景详解
1. 对聚合结果进行过滤
查询平均身高超过170cm的部门
SELECT dept_id, AVG(height) AS avg_height FROM students GROUP BY dept_id HAVING AVG(height) > 170;
2. 使用字段别名过滤
使用SELECT中定义的别名
SELECT dept_id, COUNT(*) AS student_count FROM students GROUP BY dept_id HAVING student_count > 3;
3. 多条件组合过滤
查询学生数超过2人且平均年龄小于23岁的部门
SELECT dept_id, COUNT(*) AS count, AVG(age) AS avg_age FROM students GROUP BY dept_id HAVING count > 2 AND avg_age < 23;
实际应用示例
示例1:销售数据分析
查询总销售额超过10万元的销售员
SELECT salesperson_id, SUM(amount) AS total_sales FROM orders GROUP BY salesperson_id HAVING total_sales > 100000;
示例2:学生成绩统计
查询及格率超过80%的课程
SELECT course_id, COUNT(*) AS total, SUM(CASE WHEN score >= 60 THEN 1 ELSE 0 END) AS pass_count, CONCAT(ROUND(SUM(CASE WHEN score >= 60 THEN 1 ELSE 0 END)/COUNT(*)*100,2),'%') AS pass_rate FROM exam_results GROUP BY course_id HAVING pass_count/total > 0.8;
示例3:用户活跃度分析
查询月活跃天数超过15天的高活跃用户
SELECT user_id, COUNT(DISTINCT DATE(login_time)) AS active_days FROM user_logins WHERE YEAR(login_time) = 2023 AND MONTH(login_time) = 6 GROUP BY user_id HAVING active_days > 15;
性能优化建议
1. 优先使用WHERE过滤:尽可能在WHERE阶段减少数据量,减少分组压力
2. 避免在HAVING中重复计算:
不推荐
HAVING COUNT(*)/SUM(amount) > 0.1
推荐
HAVING ratio > 0.1
3. 合理使用索引:确保GROUP BY和WHERE中的字段有适当索引
4. 限制分组数据量:先通过WHERE缩小数据范围再分组
常见错误与解决方案
错误类型 | 错误示例 | 正确示例 | 说明 |
---|---|---|---|
1. WHERE中使用聚合函数 | SELECT dept_id FROM students WHERE AVG(age) > 20 | SELECT dept_id FROM students GROUP BY dept_id HAVING AVG(age) > 20 | WHERE子句不能使用聚合函数,必须使用GROUP BY分组后,用HAVING对分组结果进行过滤 |
2. 混淆过滤顺序 | SELECT product_id, COUNT(*) FROM orders GROUP BY product_id HAVING status = 'completed' | SELECT product_id, COUNT(*) FROM orders WHERE status = 'completed' GROUP BY product_id | HAVING用于过滤分组后的结果,而WHERE用于过滤原始数据。status应该在分组前用WHERE过滤 |
3. 忽略NULL值影响 | SELECT dept_id, COUNT(*), COUNT(height) FROM students GROUP BY dept_id HAVING COUNT(*) != COUNT(height) | 此示例本身是正确的,用于演示NULL值的影响 | COUNT(*)统计所有行数,COUNT(height)只统计非NULL值。两者不等说明存在NULL值 |
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程