目录
[🎯 从问题出发:为什么我们需要子查询?](#🎯 从问题出发:为什么我们需要子查询?)
[🔍 子查询本质:查中间结果](#🔍 子查询本质:查中间结果)
[📚 子查询的分类](#📚 子查询的分类)
[✅ 非相关子查询(Non-correlated Subquery)](#✅ 非相关子查询(Non-correlated Subquery))
[🔄 相关子查询(Correlated Subquery)](#🔄 相关子查询(Correlated Subquery))
[相关 vs 非相关子查询](#相关 vs 非相关子查询)
🎯 从问题出发:为什么我们需要子查询?
假设你有一个"学生表" students
,像这样:
id | name | age | score |
---|---|---|---|
1 | 小明 | 18 | 90 |
2 | 小红 | 17 | 95 |
3 | 小刚 | 18 | 87 |
你想问这样一个问题:
"谁的分数是全班最高的?"
第一性思维问自己:我们需要两个信息:
-
最高分是多少?(最大值)
-
哪个学生的分数是这个最大值?
你发现:这不是一步能查到的。
你得先"查一个中间结果",再用这个结果查最终的目标。
🔍 子查询本质:查中间结果
定义:
子查询(subquery)就是一个嵌套在 SQL 里的"小查询",用来提前计算出你需要的"中间值",再交给外层的大查询使用。
就像数学题里你先算出 x = 5
,再把这个 x
代入下一个步骤一样。

📚 子查询的分类
按"是否依赖外层查询"
✅ 非相关子查询(Non-correlated Subquery)
定义:这个子查询完全可以单独运行,不依赖外层查询的数据。
sql
SELECT ...
FROM ...
WHERE 某列 [操作符] (子查询);
子查询:
sql
(SELECT ... FROM ... WHERE ...)
特点:
-
子查询放在圆括号里;
-
通常出现在
WHERE
、FROM
或SELECT
语句中; -
可以先执行,执行完再带入主查询。
1️⃣ 用在 WHERE
条件里
比如:查出分数等于全班最高分的学生
sql
SELECT name
FROM students
WHERE score = (SELECT MAX(score) FROM students);
子查询结果是一个"标量"(single value),主查询用它来比较筛选。
2️⃣ 用在 FROM
子句中(表子查询)
把子查询当作一个临时表(derived table)来看待,用来做进一步的查询
sql
SELECT *
FROM (SELECT * FROM students WHERE score > 90) AS top_students;
这个例子中:
-
子查询选出成绩大于 90 的人;
-
外层查询可以对它排序、分页、分组等等。
本质:把子查询变成一张临时表使用。
注意:每当你在子查询中创建了一个临时表,你必须用别名参考该表。在该例中,我们为这个临时表命名为top_students;
3️⃣ 用在 SELECT
中(计算列)
为每一行额外查询一个值(但子查询结果是固定的,不会根据外层变)
sql
SELECT name, score,
(SELECT AVG(score) FROM students) AS avg_score
FROM students;
-
每一行都显示学生自己的成绩,以及"全班平均分"
-
这个
AVG(score)
是一次性执行的,和外层无关
注意事项
子查询必须返回"合适的结果数量"
比如:
-
如果你写
= (SELECT ...)
,就必须保证子查询只返回一行一列 -
如果子查询返回多行,用
IN
或EXISTS
🔄 相关子查询(Correlated Subquery)
定义:子查询不能单独运行,它的执行依赖于外层每一行的内容。
sql
SELECT ...
FROM 表A a
WHERE 某条件操作符 (
SELECT ...
FROM 表B b
WHERE b.列 = a.列
);
⚙️ 执行过程:
-
外层取出第一行(如
a1
) -
子查询执行一遍,带入
a1
的值 -
判断是否满足条件
-
重复步骤 1~3,直到遍历所有外层行
和循环查差不多,但由数据库优化器控制流程。
1️⃣ 用在 WHERE
子句里
找出分数比自己同龄人平均分高的学生:
sql
SELECT name, age, score
FROM students s1
WHERE score > (
SELECT AVG(score)
FROM students s2
WHERE s2.age = s1.age
);
-
子查询里的
s1.age
就来自外层; -
所以每个年龄段单独算平均分再比较。
2️⃣ 用在 SELECT
字段中(作为计算列)
给每个学生显示"自己年级的平均分":
sql
SELECT name, age, score,
(SELECT AVG(score) FROM students s2 WHERE s2.age = s1.age) AS avg_score
FROM students s1;
-
外层是每个学生;
-
内层是"拿着你的年龄,查出平均分";
-
每一行都有自己"定制的"子查询结果。
3️⃣ 用在 EXISTS
判断中
找出有"重名"的学生:
sql
SELECT name
FROM students s1
WHERE EXISTS (
SELECT 1
FROM students s2
WHERE s1.name = s2.name AND s1.id <> s2.id
);
-
EXISTS
判断子查询是否返回值; -
子查询中引用了外层的
s1.name
; -
所以是相关子查询。
相关 vs 非相关子查询
项目 | 非相关子查询 | 相关子查询 |
---|---|---|
是否依赖外层数据 | ❌ 不依赖 | ✅ 依赖 |
能否单独执行 | ✅ 可以 | ❌ 不行 |
执行次数 | 1 次 | 多次(每行一次) |
使用场景 | 全局计算 / 固定值 | 动态对比 / 局部判断 |
性能 | 较高 | 较低(需优化) |
一句话总结:
子查询(Subquery)就是 SQL 中处理"复杂逻辑拆成两步"的工具,像数学里的中间变量,能帮助你更自然地表达"先求出一个值,再用它继续筛选"的需求。