必须用HAVING而非WHERE过滤分组聚合结果,因WHERE在GROUP BY前执行且不支持聚合函数;正确做法是用标量子查询(SELECT AVG(score) FROM student_score)计算全校均值,在HAVING中比较。

直接用子查询嵌套即可,但必须注意 WHERE 子句里不能直接用聚合函数,这是最常见的卡点。
为什么不能写 WHERE AVG(score) > AVG(score)?
SQL 执行顺序决定了:WHERE 在 GROUP BY 之前执行,此时 AVG(score) 还没按班级分组计算,更不存在“全校平均分”这个标量值。强行这么写会报错 ERROR 1111 (HY000): Invalid use of group function。
解决办法是把全校平均分提前算好,作为独立值参与比较——这正是子查询的典型用途。
正确写法:在 WHERE 中嵌套标量子查询
假设表结构为 student_score(class_id, score),目标是找出平均分高于全校均值的班级 ID:
SELECT class_id FROM student_score GROUP BY class_id HAVING AVG(score) > (SELECT AVG(score) FROM student_score);
这里的关键是:(SELECT AVG(score) FROM student_score) 是一个**标量子查询**(只返回单个数值),它在外部查询的 HAVING 阶段被当作常量使用。注意必须用 HAVING 而不是 WHERE,因为过滤的是分组后的聚合结果。
- 别误用
WHERE:它作用于原始行,无法访问AVG(score) - 子查询不加
GROUP BY:全校平均是整体统计,不需要分组 - 子查询可单独运行验证:先执行
SELECT AVG(score) FROM student_score看数值是否合理
如果还要查班级名称,得关联班级表
当班级信息在另一张表 class_info(id, name) 里时,不能直接在 SELECT 中写 class_info.name 而不加 JOIN —— 这会导致笛卡尔积或缺失数据。
安全做法是用子查询结果作为临时条件,再做连接:
SELECT c.name, t.avg_class_score FROM class_info c INNER JOIN ( SELECT class_id, AVG(score) AS avg_class_score FROM student_score GROUP BY class_id HAVING AVG(score) > (SELECT AVG(score) FROM student_score) ) t ON c.id = t.class_id;
注意两点:
- 子查询必须给聚合字段起别名(如
avg_class_score),否则外部引用会失败 - JOIN 条件用
c.id = t.class_id,确保班级主键和分数表中的外键对齐 - 避免在子查询里多查无关字段,影响性能——只要
class_id和聚合值就够了
真正容易被忽略的是子查询的执行时机:它在每轮 HAVING 判断前只执行一次(大多数数据库会自动优化为单次计算),但如果子查询里有相关列(比如引用了外部表字段),就会退化为相关子查询,性能急剧下降。本例中没有相关性,所以放心用。

















