子查询是在SQL语句中嵌套的SELECT查询,可用于WHERE、FROM、SELECT列表和HAVING子句;标量子查询必须返回0或1行1列,否则需改用IN或EXISTS。

MySQL 中的子查询,就是在一个 SQL 语句内部嵌套另一个 SELECT 查询。它让复杂逻辑可以分步表达,比如“查出销售额高于平均值的员工”,不用先算平均值再手动填数,一条语句就能搞定。
子查询能用在哪些位置
子查询最常出现在这几个地方:
-
WHERE 子句中:用于条件判断,比如
WHERE salary > (SELECT AVG(salary) FROM emp) -
FROM 子句中:作为临时表(又叫派生表),例如
SELECT * FROM (SELECT id, name FROM user WHERE status=1) AS active_users -
SELECT 列表中:返回单个值,常用于补充字段,如
SELECT name, (SELECT COUNT(*) FROM order WHERE user_id=u.id) AS order_count FROM user u - HAVING 子句中:配合分组使用,比如统计部门平均薪资高于公司平均的部门
必须注意的三个限制
子查询不是哪里都能随便写的,几个关键约束要记牢:
-
标量子查询(用在 WHERE 或 SELECT 中)必须返回 0 或 1 行、1 列,否则会报错。如果不确定行数,可用
IN或EXISTS - WHERE 中用 =、>、;想查多个值,改用
IN (SELECT ...) -
FROM 中的子查询必须起别名,否则 MySQL 会报语法错误,例如
FROM (SELECT ...) t,t就是必需的别名
IN、EXISTS 和 JOIN 的选择建议
当需要“查 A 表中在 B 表存在的记录”这类需求时,有三种写法,适用场景不同:
-
IN + 子查询:适合 B 表结果集小、且字段有索引,例如
SELECT * FROM user WHERE id IN (SELECT user_id FROM log WHERE type='login') - EXISTS:适合 B 表大、且关联条件能走索引,因为它是对外表每行做半连接判断,找到一个就停,效率通常更高
- JOIN:如果还要取 B 表的字段,或者需要去重/聚合,直接 JOIN 更清晰、也更容易优化
提高可读性和性能的小技巧
子查询写多了容易绕晕,也容易慢。几个实用提醒:
- 给子查询起有意义的别名,比如
(SELECT MAX(create_time) FROM audit_log WHERE target_id=u.id) AS last_audit - 避免在 WHERE 中写相关子查询(即引用外部表字段)且没加索引,可能造成全表扫描
- 用
EXPLAIN看执行计划,确认子查询是否被转成高效联接,特别是 MySQL 5.6+ 支持子查询物化和半连接优化 - 逻辑特别复杂的,不妨拆成临时表或 CTE(MySQL 8.0+ 支持),比多层嵌套更易维护










