子查询必须放在WHERE条件中用于过滤,而非SELECT列表中直接比较;正确写法是WHERE salary > (SELECT AVG(salary) FROM employees),该子查询返回单值标量,若返回多行会报错,且需注意NULL值、相关子查询及索引优化等问题。

子查询必须写在 WHERE 条件里,不能放 SELECT 列表中直接比较
很多人一上来就写 SELECT name, salary > (SELECT AVG(salary) FROM employees),这只会返回布尔值,不是你要的员工记录。子查询在这里必须作为过滤条件,而不是计算字段。
正确做法是把子查询放在 WHERE 右侧,和 salary 做数值比较:
SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
- 子查询
(SELECT AVG(salary) FROM employees)在 WHERE 执行前先算出一个标量值(比如 8245.67),整个语句才变成WHERE salary > 8245.67 - 如果子查询返回多行(比如漏写了
AVG直接查了所有工资),会报错Subquery returns more than 1 row - MySQL、PostgreSQL、SQL Server 都支持这种单值子查询用法;SQLite 也支持,但不支持相关子查询嵌套过深
用 JOIN 替代子查询不一定更快,尤其数据量小时
有人觉得“JOIN 总比子查询快”,于是写成:
SELECT e.name, e.salary FROM employees e CROSS JOIN (SELECT AVG(salary) AS avg_sal FROM employees) a WHERE e.salary > a.avg_sal;
这看似避免了重复计算,但实际执行时,优化器往往把两种写法重写成等价计划。真正影响性能的是索引和统计信息,不是语法形式。
- 对
salary字段建索引(CREATE INDEX idx_salary ON employees(salary))比换写法更有效 - 如果表很大且
AVG()涉及大量行,子查询本身会被缓存一次,不会反复执行 - JOIN 写法在某些旧版 MySQL(如 5.6)中可能触发临时表,反而变慢
WHERE 中的子查询不能引用外部表字段(除非是相关子查询)
如果误写成:
SELECT name, salary FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept_id = e.dept_id);
这就成了「相关子查询」——每次判断一个员工时,都重新算一遍其部门平均工资。它能运行,但目的已变成“找高于本部门平均工资的员工”,不是原题要求的“全公司平均”。
- 原需求只要全公司平均,子查询里
FROM employees就够了,别加WHERE或JOIN - 相关子查询性能差很多,特别是没在
dept_id上建索引时,可能触发全表扫描多次 - 错误提示不会立刻出现,但结果完全不对——这是最隐蔽的坑
NULL 值会让 AVG() 和比较行为变微妙
AVG() 默认忽略 NULL 值,但如果整列都是 NULL,它返回 NULL;而 salary > NULL 的结果永远是 UNKNOWN,该行被过滤掉——最终查不到任何人,连提示都没有。
- 先确认数据质量:
SELECT COUNT(*), COUNT(salary) FROM employees,看非空数量是否为 0 - 保险写法是加
COALESCE:WHERE salary > COALESCE((SELECT AVG(salary) FROM employees), 0) - 但要注意:用 0 填充可能让低薪员工也被选中,得根据业务决定是报错还是设默认值
子查询本身很简单,真正容易出问题的地方都在数据状态和上下文约束里——比如 AVG() 遇到全 NULL、子查询意外变成相关式、或者以为换种写法就能绕过执行计划限制。










