现在你已经准备好下一步了——来搞搞 SQL 里的嵌套查询。今天我们来聊聊它到底是啥,有啥用,分几种类型,以及为啥在实际开发中很有用。
嵌套查询(或者叫子查询)其实就是写在另一个 SQL 查询里的 SQL 查询。有点像套娃或者包菜:外面有个主查询,里面藏着一个更小的查询。子查询会先执行,然后它的结果会被外面的主查询用上(有时候主查询也叫“主查询”)。
咱们举个例子来看看:
例子 1:怎么用的
我们有一张表叫 students,数据如下:
| id | 名字 | 年龄 | 小组_id |
|---|---|---|---|
| 1 | 爱丽丝 | 20 | 1 |
| 2 | 鲍勃 | 22 | 2 |
| 3 | 克拉克 | 21 | 1 |
| 4 | 迪娜 | 23 | 3 |
| 5 | 艾米莉亚 | 22 | 2 |
还有一张表叫 groups,里面存着小组的名字:
| id | 名称 |
|---|---|
| 1 | 数学班 |
| 2 | 物理班 |
| 3 | 文学班 |
如果我们想知道学生都在哪些小组学习,可以用嵌套查询:
SELECT 名称
FROM groups
WHERE id IN (
SELECT 小组_id
FROM students
WHERE 年龄 > 21
);
这里发生了什么?
嵌套查询:
SELECT 小组_id
FROM students
WHERE 年龄 > 21
这个查询会选出所有年龄大于 21 岁学生的 小组_id。结果是小组 id 的列表,比如 [2, 3]。
主查询:
SELECT 名称
FROM groups
WHERE id IN ([子查询结果])
这个查询用子查询的结果,返回 id 是 2 或 3 的小组名称。
结果:
物理班
文学班
还是有点懵?很正常。别急,咱们慢慢来。
先来个简单的想法——SELECT 查询的结果其实就是一个虚拟表。它有列,也有行,不就是表嘛?
既然查询结果是表,那它就能在需要表的地方用,比如 JOIN 操作,或者更复杂的 SQL 结构里。
它没有名字,这确实有点麻烦。但表达式的列也没名字,我们一般给它们起个 alias(别名)。虚拟表也能这样搞。
后面几节课会详细讲——现在先不剧透 :P
嵌套查询的好处
让复杂问题变简单。有时候一张表里没有你想要的所有信息。子查询可以把查询分两步:先查中间结果,再用它查最终数据。
搞定中间结果。嵌套查询很适合你需要先做点计算再处理数据的时候。比如找最小值、算总和啥的。
让代码更好读。嵌套查询让代码结构更清晰,尤其是你在处理大表和复杂逻辑时。
嵌套查询的主要类型
嵌套查询可以写在 SQL 查询的不同地方。根据你写在哪儿,分几种类型。
- 写在
SELECT里的子查询。子查询在列列表里,用来算值。比如你想在结果里加一列。
例子——加一列显示学生的最大年龄:
SELECT 名字, 年龄,
(SELECT MAX(年龄) FROM students) AS 最大年龄
FROM students;
结果:
| 名字 | 年龄 | 最大年龄 |
|---|---|---|
| 爱丽丝 | 20 | 23 |
| 鲍勃 | 22 | 23 |
| 克拉克 | 21 | 23 |
| 迪娜 | 23 | 23 |
| 艾米莉亚 | 22 | 23 |
- 写在
FROM里的子查询。子查询当临时表用。如果你要先聚合或变换数据,这就很方便。
例子——统计每个小组学生的平均年龄:
SELECT tmp.小组_id, tmp.平均年龄
FROM (
SELECT 小组_id, AVG(年龄) AS 平均年龄
FROM students
GROUP BY 小组_id
) AS tmp -- 给临时表起个别名 tmp
WHERE tmp.平均年龄 > 21;
结果:
| 小组_id | 平均年龄 |
|---|---|
| 2 | 22.0 |
| 3 | 23.0 |
- 写在
WHERE和HAVING里的子查询。子查询可以当条件用,过滤行。经常用来判断 - 有没有记录,或者比较值。
例子——找出比平均年龄大的学生:
SELECT 名字, 年龄
FROM students
WHERE 年龄 > (
SELECT AVG(年龄)
FROM students
);
结果:
| 名字 | 年龄 |
|---|---|
| 鲍勃 | 22 |
| 迪娜 | 23 |
| 艾米莉亚 | 22 |
用嵌套查询的优点
更灵活:嵌套查询让你能搞定更复杂的数据结构。
分步骤处理:可以把逻辑拆成子查询,代码更好读。
随时用中间数据:你可以“边查边处理”,不用在数据库里建临时表。
GO TO FULL VERSION