子查询在where中用于单值比较需确保一行一列,多行用in或exists;from中作派生表须加别名;update/delete中不可直接引用目标表,需嵌套派生表或改用join。

WHERE 子句里用 SELECT 返回单值子查询
这是最常见也最安全的用法:子查询必须返回且仅返回一行一列,否则会报错 Subquery returns more than 1 row。适用于条件过滤,比如查“工资高于平均工资的员工”。
-
WHERE salary > (SELECT AVG(salary) FROM employees)—— 正确,聚合函数保证单值 -
WHERE dept_id = (SELECT dept_id FROM departments WHERE name = 'Tech')—— 要求name唯一,否则可能报错 - 如果不确定唯一性,改用
IN或加LIMIT 1(但后者逻辑可能出错,慎用) - MySQL 8.0+ 支持标量子查询自动转为
NULL(当无结果时),但老版本会报错Subquery returns no rows
用 IN 或 EXISTS 处理多行结果子查询
当子查询要返回多行(比如一批 ID 列表),= 就不适用了,得换操作符。二者语义不同,性能差异明显。
-
WHERE user_id IN (SELECT id FROM banned_users)——IN适合小结果集;若子查询结果含NULL,整条条件会判为UNKNOWN,该行被过滤掉 -
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id AND o.status = 'paid')—— 推荐用于关联存在性判断;不依赖子查询返回值内容,只看是否“有匹配行”,通常比IN更快,尤其外层大表、内层可走索引时 - 避免
NOT IN (SELECT ...):只要子查询结果里有一个NULL,整个条件恒为FALSE,结果为空——这是线上事故高发点
FROM 子句中用子查询当临时表(派生表)
MySQL 要求所有子查询在 FROM 中必须有别名,否则报错 Every derived table must have its own alias。适合做中间聚合或筛选后再连接。
SELECT t.name, t.cnt FROM (SELECT dept_id, COUNT(*) AS cnt FROM employees GROUP BY dept_id) AS t JOIN departments d ON t.dept_id = d.id- 子查询里的字段不能直接用外层别名引用(如
t.dept_id在子查询内部不可见) - MySQL 5.7 及以前,派生表默认物化(即先执行完再参与连接),可能导致无法使用索引;8.0+ 引入“派生合并优化”,可将子查询“展开”进外层,但需满足条件(如无
GROUP BY、DISTINCT等) - 复杂逻辑建议改用 CTE(
WITH),可读性和优化器支持更好(MySQL 8.0+)
UPDATE / DELETE 语句里嵌套子查询的限制
MySQL 不允许在 UPDATE 或 DELETE 的 WHERE 中直接引用目标表,否则报错 You can't specify target table for update in FROM clause。这是新手最容易卡住的地方。
- 错误写法:
DELETE FROM users WHERE id IN (SELECT user_id FROM logs WHERE action = 'spam')—— 如果logs和users是同一张表,就挂了 - 绕过方法:把子查询再包一层
FROM子查询(即做成派生表),例如:DELETE FROM users WHERE id IN (SELECT id FROM (SELECT user_id AS id FROM logs WHERE action = 'spam') AS tmp) - 更稳妥的做法是用
JOIN重写:DELETE u FROM users u JOIN logs l ON u.id = l.user_id WHERE l.action = 'spam' - 注意:
UPDATE同理,且涉及多表更新时,务必确认JOIN条件能准确定位,否则可能误删/误改
EXPLAIN 看 type 和 rows 列,比语法正确更重要。










