子查询是MySQL中解决多表逻辑依赖的刚需手段,必须用括号包裹且WHERE中的标量子查询须返回单值;IN适用于小结果集,EXISTS支持外层关联、适合大结果集;相关子查询有N×M性能风险。

子查询在 MySQL 中不是“可选技巧”,而是解决多表逻辑依赖的刚需手段。只要需要基于某次查询结果再做筛选、计算或关联,就绕不开它。
子查询必须用括号包裹,且不能加 LIMIT
MySQL 要求所有子查询必须用 () 明确包裹,否则直接报错 ERROR 1064。另外,子查询内部不能直接使用 LIMIT(除非配合 ORDER BY 且位于派生表位置)。
- ✅ 正确:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100) - ❌ 错误:
SELECT * FROM users WHERE id IN SELECT user_id FROM orders LIMIT 10(缺括号 + 多余 LIMIT) - ⚠️ 特殊可用:
SELECT * FROM (SELECT * FROM logs ORDER BY ts DESC LIMIT 5) AS recent(仅限 FROM 子句中的派生表)
WHERE 中的标量子查询必须返回单值
当子查询出现在 WHERE 条件右侧(如 =、>、BETWEEN 等)时,它必须只返回一行一列,即“标量”。否则会触发 ERROR 1242: Subquery returns more than 1 row。
- 用
MAX()/MIN()/AVG()聚合确保单值:WHERE salary > (SELECT AVG(salary) FROM employees) - 用
LIMIT 1强制截断(慎用,语义可能不严谨):WHERE dept_id = (SELECT id FROM departments WHERE name = 'HR' LIMIT 1) - 避免写成:
WHERE status = (SELECT status FROM audits WHERE ref_id = 123)(若有多条 audit 记录就崩)
IN 和 EXISTS 的语义与性能差异很实际
IN 检查值是否在结果集中,适合子查询结果集小、主表大;EXISTS 是半连接语义,只关心是否存在匹配行,适合子查询结果集大、或需关联外层字段时。
SELECT name FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'shipped' );
-
IN子查询无法引用外部表字段:... WHERE id IN (SELECT customer_id FROM orders WHERE status = 'shipped')(只能查全部) -
EXISTS子查询可关联外部字段(如上例中的c.id),这是它不可替代的关键点 - 若子查询结果为空,
IN返回空集,EXISTS返回FALSE—— NULL 处理逻辑不同,会影响NOT IN的行为(NOT IN遇到 NULL 直接整个条件为 UNKNOWN)
子查询最易被忽略的其实是执行顺序:MySQL 通常先执行外层查询的驱动表,再对每一行执行子查询(相关子查询),这意味着 N×M 次扫描风险真实存在。别只盯着语法对不对,先看执行计划里的 DEPENDENT SUBQUERY 出现了几次。









