SQL:子查询(subqueries)

目录

[🎯 从问题出发:为什么我们需要子查询?](#🎯 从问题出发:为什么我们需要子查询?)

[🔍 子查询本质:查中间结果](#🔍 子查询本质:查中间结果)

[📚 子查询的分类](#📚 子查询的分类)

[✅ 非相关子查询(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

你想问这样一个问题:

"谁的分数是全班最高的?"

第一性思维问自己:我们需要两个信息:

  1. 最高分是多少?(最大值)

  2. 哪个学生的分数是这个最大值?

你发现:这不是一步能查到的。

你得先"查一个中间结果",再用这个结果查最终的目标。

🔍 子查询本质:查中间结果

定义:

子查询(subquery)就是一个嵌套在 SQL 里的"小查询",用来提前计算出你需要的"中间值",再交给外层的大查询使用。

就像数学题里你先算出 x = 5,再把这个 x 代入下一个步骤一样。

📚 子查询的分类

按"是否依赖外层查询"

✅ 非相关子查询(Non-correlated Subquery)

定义:这个子查询完全可以单独运行,不依赖外层查询的数据。

sql 复制代码
SELECT ...
FROM ...
WHERE 某列 [操作符] (子查询);

子查询:

sql 复制代码
(SELECT ... FROM ... WHERE ...)

特点:

  • 子查询放在圆括号里;

  • 通常出现在 WHEREFROMSELECT 语句中;

  • 可以先执行,执行完再带入主查询。

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 ...),就必须保证子查询只返回一行一列

  • 如果子查询返回多行,用 INEXISTS


🔄 相关子查询(Correlated Subquery)

定义:子查询不能单独运行,它的执行依赖于外层每一行的内容。

sql 复制代码
SELECT ...
FROM 表A a
WHERE 某条件操作符 (
  SELECT ...
  FROM 表B b
  WHERE b.列 = a.列
);

⚙️ 执行过程:

  1. 外层取出第一行(如 a1

  2. 子查询执行一遍,带入 a1 的值

  3. 判断是否满足条件

  4. 重复步骤 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 中处理"复杂逻辑拆成两步"的工具,像数学里的中间变量,能帮助你更自然地表达"先求出一个值,再用它继续筛选"的需求。

相关推荐
王有品2 小时前
Spring MVC 多个拦截器的执行顺序
数据库·spring·mvc
极小狐2 小时前
如何使用极狐GitLab 的外部状态检查功能?
数据库·ci/cd·gitlab·devops·mcp
Leo.yuan2 小时前
数据仓库建设全解析!
大数据·数据库·数据仓库·数据分析·spark
活跃的煤矿打工人3 小时前
【星海出品】分布式存储数据库etcd
数据库·分布式·etcd
文牧之3 小时前
PostgreSQL的扩展 pgcrypto
运维·数据库·postgresql
老友@4 小时前
小集合 VS 大集合:MySQL 去重计数性能优化
数据库·mysql·性能优化
声声codeGrandMaster4 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
熏鱼的小迷弟Liu5 小时前
【Redis】Redis Zset实现原理:跳表+哈希表的精妙设计
数据库·redis·散列表
淋一遍下雨天6 小时前
Spark Streaming核心编程总结(四)
java·开发语言·数据库