AVG()全表均值需用非相关子查询实现,即SELECT中嵌套(SELECT AVG(score) FROM students),确保只执行一次;分类须在主查询用CASE WHEN对比当前行与该均值,并显式处理NULL及精度、跨库兼容性问题。

用 AVG() 在主查询中直接计算全表平均值
非相关子查询的关键是「不依赖外部行」,所以它必须能独立执行。最直接的方式是在 SELECT 列表里嵌一个子查询,比如:
SELECT score, name, (SELECT AVG(score) FROM students) AS avg_score FROM students;这个子查询只运行一次,结果被广播到每一行。注意:不能写成
SELECT AVG(score) FROM students GROUP BY id——那会按行分组,失去“全表”意义。
根据与全表均值的大小关系打标签(CASE WHEN + 子查询)
分类逻辑要放在主查询里,用 CASE WHEN 对比当前行值和子查询结果。常见错误是把子查询写进 WHERE 或 GROUP BY——那会触发多次执行或语法报错。
SELECT name, score,
CASE
WHEN score > (SELECT AVG(score) FROM students) THEN 'above_avg'
WHEN score < (SELECT AVG(score) FROM students) THEN 'below_avg'
ELSE 'equal_avg'
END AS category
FROM students;这里两个子查询实际会被优化为一次执行(主流数据库如 PostgreSQL、MySQL 8.0+、SQL Server 都支持),但语义上仍是非相关子查询。
避免重复写子查询:用 WITH 提前定义均值变量
虽然非相关子查询本身高效,但重复书写易出错、难维护。更清晰的做法是用 CTE 抽出来:
WITH stats AS (
SELECT AVG(score) AS global_avg FROM students
)
SELECT s.name, s.score,
CASE
WHEN s.score > stats.global_avg THEN 'above_avg'
WHEN s.score < stats.global_avg THEN 'below_avg'
ELSE 'equal_avg'
END AS category
FROM students s
CROSS JOIN stats;CROSS JOIN stats 确保每行都带上这个标量值。相比反复写子查询,CTE 更易读、调试友好,且在复杂查询中便于复用其他聚合结果(如 MIN/MAX)。
注意 NULL 值和数据类型对比较的影响
如果 score 列含 NULL,AVG() 会自动忽略它们,但比较时 score > NULL 结果是 UNKNOWN,导致该行进不到任何 WHEN 分支,最终落入 ELSE——这容易误判。务必检查:
-
SELECT COUNT(*), COUNT(score) FROM students确认非空行数 - 在
CASE中显式处理score IS NULL分支 - 若
score是DECIMAL而子查询返回FLOAT,某些数据库(如 older MySQL)可能隐式转换引发精度偏差,建议统一用ROUND(AVG(score), 2)
真正麻烦的是跨数据库兼容性:AVG() 在空表中返回 NULL,而 CASE 对 NULL > NULL 的判断各库不一致;生产环境上线前,得用空表测一遍 category 字段是否为 NULL 还是掉进 ELSE。

















