# P40 40. 基础-多表查询-外连接

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

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

## P40 40. 基础-多表查询-外连接

- 原视频：https://www.bilibili.com/video/BV1Kr4y1i7ru/?p=40（p=40），时长 7:27
- 讲义：5.4 外连接 p77–78（公开镜像；按主题对应，切点 notexact）

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

### 一句话理解

外连接在内连接的基础上，保留某一侧“配不上”的行，另一侧补 NULL。

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

视频用左、右外连接演示「配不上的行怎么保留」。

| 视频里 / 原课字段 | 本站对应 |
|---|---|
| 讲义里没有部门的是陈友谅（dept_id 为空） | 本站是小李（dept_id 为空），左连接后部门列是 NULL |
| emp LEFT JOIN dept | employee LEFT JOIN department（写法相同） |

看完应能：能解释 LEFT JOIN 出来的 NULL 是「没配上」，不是数据坏了

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

### 步骤

1. 先想清楚要保住谁。要“所有员工，包括没部门的”，员工在左就用 LEFT JOIN；要“所有部门，包括没人的”，部门放右边用 RIGHT JOIN。
2. ON 里写连接条件，也可以写“只影响能不能配上”的过滤。过滤放 ON 还是 WHERE，取决于你想保留哪些左行：想保留全部员工、只给研发部的人标上部门，就把 `d.name = '研发部'` 放 ON，其他员工照样出现、部门列是 NULL；只要研发部员工，就把条件放 WHERE（或者干脆用内连接）。两者是不同的问题，不是对错。判断办法：把条件从 ON 挪到 WHERE，如果连本来没配上的左行也一起消失，说明这个过滤本身在筛员工，不只是筛匹配。
3. 验证时数行数：左连接保证左表每一行至少出现一次，所以结果行数不会少于左表行数；本例 `department.id` 是主键、一名员工最多配上一个部门，结果才恰好 4 行。若左表一行配上多个右表行，行数变多（4 行变 5 行以上）是正常放大，不是写错；变成 3 行才说明有左行被过滤掉了。
4. 统计时要分清 COUNT(*) 和 COUNT(右表列)：前者数结果行，后者只数真正匹配上的行。

### SQL 示例

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

SELECT e.name AS 员工, d.name AS 部门
FROM employee e
LEFT JOIN department d ON e.dept_id = d.id
ORDER BY e.id;

SELECT e.name AS 员工, d.name AS 部门
FROM employee e
RIGHT JOIN department d ON e.dept_id = d.id
ORDER BY d.id;
```

说明：左连接 4 行，第 4 行是“小李 / NULL”，因为他的 dept_id 为空；本例 department.id 是主键，一名员工最多配一个部门，所以没有一对多放大，行数才正好等于左表的 4 行。右连接换方向保部门，也是 4 行，最后一行“NULL / 运营部”，因为运营部还没人。想让运营部那行出现在“部门在左”的写法里，把表序换成 department LEFT JOIN employee 即可。 右连接也可以调换表序写成 LEFT JOIN，结果相同；团队里通常统一用 LEFT，方向更一致。

### 易错点

- LEFT JOIN 后在 WHERE 写 d.id IS NOT NULL 会滤掉 NULL 行，等价于内连接；要专门找“没部门的员工”应写 WHERE d.id IS NULL。
- COUNT(*) 数的是结果行（4），COUNT(d.id) 数的是匹配上的部门（3）；统计“多少人有部门”必须用后者。
- MySQL 没有 FULL OUTER JOIN；要两边都保留只能 LEFT UNION RIGHT，并注意去重。

### 30 秒速记

左保左、右保右；配不上补 NULL；过滤放 ON 还是 WHERE 看要保留哪些左行；COUNT(*)≠COUNT(右表列)。

### 自测

1. 左连接后想只留“没有部门的员工”，条件怎么写？
   - 答：WHERE d.id IS NULL（或 d.name IS NULL），写在连接之后。
2. LEFT JOIN 结果 4 行，COUNT(*) 和 COUNT(d.id) 分别是？
   - 答：4 和 3；COUNT(列) 不数 NULL。
3. 为什么 LEFT JOIN 之后再写 d.id IS NOT NULL 会变回内连接？
   - 答：它把补出来的 NULL 行全部滤掉，剩下的就是匹配行，结果与内连接相同。

### 材料与来源

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

证据说明：本集无逐字稿（匿名 player/v2 返回 subtitles=[]、need_login_subtitle=true），并以讲义公开镜像讲义 5.4 外连接（p77–78）核对章节与主题；画面静帧只用于确认场景，不是内容边界。示例表与数据为本笔记自建，不是课程原表；不含原始帧或字幕。
