子查询
把一个查询结果当作另一个查询的条件
子查询常见出现位置:
WHERE / HAVING:作为过滤条件SELECT:作为标量表达式(每行一个结果)FROM:作为派生表(把子查询结果当临时表再关联)
按返回形态分三类
- 标量子查询:子查询返回的结果是一个数据(一行一列)
- 列子查询:返回的结果是一列(一列多行)
- 行子查询:返回的结果是一行(一行多列)
-- 查询高于平均身高的信息
-- 1 查出平均身高
-- 2 查出高于平均身高的信息
SELECT * FROM students WHERE height > (SELECT AVG(height) FROM students);
-- 查询学生的班级号能够对应的学生名字
-- 1. 查出所有的班级id
SELECT id FROM classes;
-- 2. 查出能够对应上班级好的学生信息
SELECT * FROM students WHERE cls_id in (SELECT id FROM classes);
相关子查询与 EXISTS(更贴近“是否存在”)
当你只关心“有没有匹配行”而不是“匹配值是什么”,EXISTS 往往更自然:
SELECT *
FROM classes c
WHERE EXISTS (
SELECT 1
FROM students s
WHERE s.cls_id = c.id
);
这类子查询会引用外层表(c.id),称为相关子查询。
子查询 vs JOIN(怎么选)
- 子查询:写法直观,适合“先算一个集合/一个值,再用它过滤”的思路
- JOIN:更适合“把多表信息拼到同一行”或需要返回多表字段的场景
性能上二者并非绝对谁快谁慢,关键在索引与过滤条件;写慢时优先检查连接列/过滤列索引(见 索引 / DQL)。
常见坑
IN (subquery)遇到NULL时容易出现三值逻辑带来的意外结果;需要时显式过滤NULL(见 空值判断)