MySQL 中的子查询,就是在一个 SQL 语句内部嵌套另一个 SELECT 查询。它让复杂逻辑可以分步表达,比如“查出销售额高于平均值的员工”,不用先算平均值再手动填数,一条语句就能搞定。
子查询能用在哪些位置
子查询最常出现在这几个地方:
WHERE 子句中:用于条件判断,比如WHERE salary > (SELECT AVG(salary) FROM emp)FROM 子句中:作为临时表(又叫派生表),例如
SELECT * FROM (SELECT id, name FROM user WHERE status=1) AS active_usersSELECT 列表中:返回单个值,常用于补充字段,如
SELECT name, (SELECT COUNT(*) FROM order WHERE user_id=u.id) AS order_count FROM user uHAVING 子句中:配合分组使用,比如统计部门平均薪资高于公司平均的部门
必须注意的三个限制
子查询不是哪里都能随便写的,几个关键约束要记牢:
标量子查询(用在 WHERE 或 SELECT 中)必须返回 0 或 1 行、1 列,否则会报错。如果不确定行数,可用IN或
EXISTSWHERE 中用 =、>、;想查多个值,改用
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+ 支持),比多层嵌套更易维护
