# P45 45. 基础-多表查询-列子查询

> 本页是学习笔记，不等同于视频逐字稿。原声字幕未取得（匿名接口字幕轨为空且需登录，未使用任何账号），也没有逐句讲解记录；讲义只按节与页码对应，切点未逐句确证。

> 以同版讲义的公开镜像核对章节与页码（镜像非官方出品）；非逐字稿，示例为原创补充。

## P45 45. 基础-多表查询-列子查询

- 原视频：https://www.bilibili.com/video/BV1Kr4y1i7ru/?p=45（p=45），时长 10:11
- 讲义：5.6.3 列子查询（IN / NOT IN / ANY / SOME / ALL） p82–83（公开镜像；按主题对应，切点 notexact）

- 本集练习数据：practice/p45.sql（先执行，再对照小例）

### 一句话理解

列子查询返回一列多行，等于给你一个集合：在集合里挑值用 IN，拿一个值和整个集合逐个比用 ANY / ALL。

### 跟播：视频里 ↔ 本站

视频停在列子查询的操作符表（IN / NOT IN / ANY / SOME / ALL），并用研发部、财务部对照 > ANY 与 > ALL。

| 视频里 / 原课字段 | 本站对应 |
|---|---|
| emp.salary / emp.dept_id（17 行） | employee.salary / employee.dept_id（4 行） |
| 研发部 = dept_id 1（讲义 dept 6 行） | 研发部 = dept_id 1（本站 department 3 行） |

看完应能：看到 > any 能马上换成「大于最小值」，并知道 SOME 就是 ANY

看视频时以讲义表为准，跟练时用本站表；结构相同、数字不同。

### 步骤

1. 先确认内层只查一列。多查一列就变成表子查询，IN 接不住；一列多行意味着结果可能是 0 行、1 行或很多行。
2. IN 判断「在不在这个集合里」，最常用；NOT IN 是它的反面，但集合里有 NULL 时整体变成 unknown，结果常常是 0 行。
3. ANY（与 SOME 完全同义）是「至少有一个满足」：研发部工资是 8000、10000，salary > ANY（那个子查询）只要大于 8000 就成立，也就是大于集合里的最小值。
4. ALL 是「全部都满足」：同一份数据里 salary > ALL（那个子查询）要大于 10000，也就是大于集合里的最大值。写完用行数反推，别只看语法。

### SQL 示例

```
-- 表：department(1 研发部, 2 测试部, 3 运营部)
-- employee：小林22/研发8000/上级2，小周25/研发10000/无上级，小陈30/测试12000/上级2，小李21/无部门/NULL/上级2
-- 建表与插入语句见本页练习脚本 p45.sql（独立练习库 mysqlnotes_p45，跑一次就有这两张表）

-- IN：研发部和测试部的员工
SELECT name AS 员工, dept_id AS 部门号
FROM employee
WHERE dept_id IN (
  SELECT id FROM department WHERE name IN ('研发部','测试部')
);

-- ALL：比研发部所有人都高（= 大于最大值）
SELECT name AS 员工, salary AS 工资
FROM employee
WHERE salary > ALL (
  SELECT salary FROM employee WHERE dept_id = 1
);

-- ANY：比研发部“任意一人”工资高即可（= 大于最小值）
SELECT name AS 员工, salary AS 工资
FROM employee
WHERE salary > ANY (
  SELECT salary FROM employee WHERE dept_id = 1
)
ORDER BY id;
```

说明：第一段内层返回 1、2 两个部门号，外层 3 行：小林、小周、小陈。第二段研发部工资是 8000、10000，> ALL 要大于 10000，只剩小陈 12000（1 行）。第三段 > ANY 只要大于 8000 就成立，小周 10000、小陈 12000 都入选（2 行）——同一份数据，ALL 与 ANY 的差别就是「全部」还是「任意一个」。记住方向：> ANY ≡ > MIN，> ALL ≡ > MAX；反过来 < ANY ≡ < MAX，< ALL ≡ < MIN。ALL 的好处是不用自己先想到 MAX，语义直接；ANY 的好处是「比其中任意一个高」这种需求能直说。

### 最小数列：3 个值讲清 ANY 与 ALL

```sql
-- 集合 {3000, 6000, 9000}：5000 比最小的大、比最大的小
SELECT 5000 > ANY (SELECT 3000 UNION SELECT 6000 UNION SELECT 9000) AS 比任意一个大,
       5000 > ALL (SELECT 3000 UNION SELECT 6000 UNION SELECT 9000) AS 比全部都大;
```

说明：结果一行：1、0。ANY 只要有一个成立就为真，门槛是最小值 3000；ALL 要求全都成立，门槛是最大值 9000。SOME 与 ANY 完全同义，写哪个都行。

预期结果：1、0（比任意一个大 = 1；比全部都大 = 0）

### 补充：空集与 NULL：两个反直觉结果（点开再看，不影响主线）

子查询为空集时：> ALL 恒为真（会返回外层所有行），> ANY 恒为假（0 行）。集合里有 NULL 时：> ALL 因为有一个 unknown 而永远不成立；> ANY 只要有一个非 NULL 比较成立就仍为真。下面这条在 MySQL 8.0.46 实测：all_empty=1、any_empty=0，两个与 NULL 比较的结果都是 unknown（IS NULL 为 1）。

```sql
SELECT (1 > ALL (SELECT salary FROM employee WHERE 1 = 0)) AS all_empty,
       (1 > ANY (SELECT salary FROM employee WHERE 1 = 0)) AS any_empty,
       ((5 > ALL (SELECT NULL)) IS NULL) AS all_null_unknown;
```

### 易错点

- NOT IN 的子查询结果含 NULL 时，整个条件永远不为真，结果 0 行；要么子查询加 WHERE dept_id IS NOT NULL，要么改用 NOT EXISTS。
- IN 后面的子查询只能返回一列；返回两列会报错，需要改成整行比较。
- 把 > ALL 读成「大于其中某一个」就全错了，那是 ANY 的语义；ALL 是「全部都要满足」。
- 以为 ANY 遇空集会全选：空集时 ANY 恒假（0 行），ALL 反而恒真。

### 30 秒速记

列子查询=一列多行；IN 在集合内；NOT IN 遇 NULL 全废；> ALL 等于大于最大值，> ANY 等于大于最小值；SOME 与 ANY 同义。

### 自测

1. NOT IN 子查询里有 NULL 会怎样？
   - 答：比较结果变 unknown，条件不成立，通常返回 0 行；先排除 NULL 或改 NOT EXISTS。
2. salary > ALL（子查询返回 8000、10000）等价于什么？
   - 答：salary > 10000，即大于集合最大值。
3. IN 和 = 该怎么选？
   - 答：子查询确定只返回一个值用 =；可能多行就用 IN，否则报 1242。

### 材料与来源

- [平台视频页] BV1Kr4y1i7ru P45（平台标题） https://www.bilibili.com/video/BV1Kr4y1i7ru/?p=45
- [官方文档] MySQL 8.0 Reference Manual — Subqueries https://dev.mysql.com/doc/refman/8.0/en/subqueries.html

证据说明：本集无逐字稿（匿名 player/v2 返回 subtitles=[]、need_login_subtitle=true），依据平台标题、讲义 p82–83「5.6.3 列子查询」与公开静帧核对：讲义操作符表列 IN / NOT IN / ANY / SOME / ALL，并注明 SOME 与 ANY 等同；案例 C 就是「比研发部其中任意一人工资高」。示例表与数据为本笔记自建，不是课程原表。
