全外连接与模拟写法
全外连接把两侧都保留:左表独有的行、两侧匹配上的行、右表独有的行,全部出现在结果里,互不匹配的位置补空。PostgreSQL 与标准 SQL 直接支持这个语法,MySQL 不支持,只能拼出来——左侧保留一次、右侧保留一次,再合并去重。拼接时要特别小心去重方式:使用会去重的合并形式,匹配行确实只出现一次,但业务上本来就重复的行也会被合并掉。
基本信息
| 适用平台 | PostgreSQL 与非 MySQL 数据库支持;MySQL 8.0 不支持该语法 |
|---|---|
| 结果构成 | 左表独有加匹配行加右表独有 |
| MySQL 模拟 | 保留左侧的结果与保留右侧的结果合并 |
| 去重注意 | 用去重形式会顺带合并业务重复行 |
| 安全改写 | 保留侧加过滤条件,把两次结果用不去重形式拼接 |
典型使用场景
- 两张表双向比对:找出只在左侧、只在右侧以及两侧都有的数据
- 数据迁移核验:源表与目标表逐键比对,确认没有漏迁或多迁
- 对账差异定位:两边都缺的、单边多的分别列出
- 配置同步检查:两边配置项的并集清单
- 用户与权限关系核对:找出孤立记录
原语与 MySQL 模拟
原语与 MySQL 模拟
-- PostgreSQL:直接写全外连接
SELECT *
FROM a
FULL OUTER JOIN b ON a.k = b.k;
-- MySQL 模拟一:用会去重的合并,匹配行只出现一次
SELECT * FROM a LEFT JOIN b ON a.k = b.k
UNION
SELECT * FROM a RIGHT JOIN b ON a.k = b.k;
-- MySQL 模拟二:更安全,用不去重形式拼接两次结果
-- (把重复来源过滤掉,避免业务重复行被合并)
SELECT * FROM a LEFT JOIN b ON a.k = b.k
UNION ALL
SELECT * FROM a RIGHT JOIN b ON a.k = b.k WHERE a.k IS NULL;
-- 双向差异清单:只关心各自独有的行
-- 左独有:保留左侧结果中右表键为空的行
-- 右独有:保留右侧结果中左表键为空的行两种模拟方式对比
| 模拟方式 | 匹配行 | 副作用 |
|---|---|---|
| 会去重的合并 | 只出现一次 | 业务上重复的行也被合并 |
| 不去重形式加过滤 | 只出现一次 | 不干扰业务重复行,改写更啰嗦 |
| 仅两次全保留再合并 | 出现两次 | 结果行数翻倍,需自行处理 |
| 单侧外连接 | 结构不等价 | 只能看一个方向,不能替代 |
全外连接结果里「匹配上的行」数量等于逻辑上两侧键交集的实际行数。用会去重的合并形式做两表核验时,如果任意一侧存在同一键的多行数据,交集部分会被去重成一到多行不等,核验结论就不可靠了。
只想知道「哪一侧多了数据」时,不必每次都拼全外连接:分别做一次外连接并筛出未匹配行,结果更直观,执行代价也更低。