子查询在select语句中必须严格匹配位置约束:where中优先用exists防null陷阱,select列中须为单行单列标量子查询,from中作派生表必须加别名。

子查询在 SELECT 语句中不是“加个括号就能用”,它必须匹配所在位置的语义约束:WHERE 要能返回布尔结果,SELECT 列要返回单值,FROM 里必须带别名——错一个就报错或静默出错。
WHERE 中用子查询筛选,优先选 EXISTS 而非 IN
直接写 IN 看似简洁,但只要子查询结果含 NULL,整条条件就变成 UNKNOWN,查不到任何数据,还不报错。
-
IN对空集或含NULL的结果敏感:比如WHERE id IN (SELECT user_id FROM orders WHERE status IS NULL)可能返回空结果,不是没数据,而是逻辑失效 -
EXISTS只判断存在性,不关心值、不惧NULL,且找到第一行就终止,性能更稳 - 子查询里用
SELECT 1是惯例,避免无谓字段解析;外层关联字段(如o.user_id = u.id)必须有索引,否则全表扫描
正确写法示例:
SELECT name FROM users u<br>WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id AND o.status = 'paid');
SELECT 列中嵌标量子查询,必须保证单行单列
放在 SELECT 列表里的子查询,数据库会为每一行执行一次,结果必须且只能是一个值,否则直接报错 ERROR 1242。
- 漏写
WHERE或关联条件(如e2.dept_id = e1.dept_id)会导致多行返回 - 相关子查询性能极敏感:10 万用户 + 每次查部门平均工资 = 执行 10 万次子查询
- 加速关键:给被关联字段建复合索引,例如
(dept_id, salary),比单列dept_id索引更有效
示例:
SELECT name,<br> (SELECT AVG(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id)<br> AS dept_avg_salary<br>FROM employees e1;
FROM 中用子查询作派生表,别名是硬性要求
把子查询当临时表用,语法强制要求别名,否则报错 ERROR 1248;而且所有字段引用都得带上这个别名前缀。
- 别名写法两种都合法:
(SELECT ...) temp或(SELECT ...) AS temp - 外部查询不能直接写
avg_score,必须写temp.avg_score - 适合先聚合再过滤的场景,比如“平均分 > 90 的班级”,比在
HAVING里套子查询更清晰
示例:
SELECT class, avg_score<br>FROM (SELECT class, AVG(score) AS avg_score FROM scores GROUP BY class) AS temp<br>WHERE temp.avg_score > 90;
最常被忽略的是相关子查询的执行次数和索引依赖——它看着像一行 SQL,实际可能是 N 次独立查询;而 IN 遇 NULL 失效这种问题,线上排查时往往卡在数据质量上,不是语法错,是逻辑断点。











