mysql不支持full outer join,需用left join+right join+union all模拟:第一段取左表全量,第二段用where a.id is null精准捕获右表独有行,字段须严格对齐且用null显式补位。

MySQL 不支持 FULL OUTER JOIN,但用 UNION 模拟是可行的,关键在于 LEFT + RIGHT 的补全逻辑要写对,漏掉 IS NULL 条件就会丢数据。
为什么不能直接写 FULL OUTER JOIN
MySQL 8.0.17 之前完全不识别该语法,报错 ERROR 1064 (42000);即使新版也仅支持语法解析(实际仍不执行),本质仍是未实现。不是版本升级就能解决的问题,得手动构造。
常见错误现象:复制 PostgreSQL 或 SQL Server 的语句直接运行,得到语法错误或空结果。
-
FULL OUTER JOIN的语义是“保留左表所有行 + 右表所有行”,NULL 填充缺失匹配 - MySQL 只有
LEFT JOIN和RIGHT JOIN,需组合使用才能覆盖全部情况 -
UNION本身去重,若业务允许重复行,必须改用UNION ALL
用 LEFT JOIN + RIGHT JOIN + UNION ALL 构造
核心思路:左连接取“左表全量 + 右表匹配部分”,右连接取“右表中没被左连接覆盖的行”(即右表独有行),再合并。
注意:右连接那部分必须过滤掉已出现在左连接结果里的右表行,否则会重复;判断依据是右表主键在左连接结果中为 NULL。
SELECT a.id, a.name, b.order_id, b.amount FROM users a LEFT JOIN orders b ON a.id = b.user_id UNION ALL SELECT a.id, a.name, b.order_id, b.amount FROM users a RIGHT JOIN orders b ON a.id = b.user_id WHERE a.id IS NULL;
说明:
- 第一段
LEFT JOIN覆盖了所有用户,含其订单(有则填值,无则NULL) - 第二段
RIGHT JOIN+WHERE a.id IS NULL精准捕获“有订单但无对应用户”的脏数据或孤儿记录 - 必须用
UNION ALL:两段结果天然不重叠(第一段a.id全非空,第二段a.id全为NULL),UNION多一次去重开销 - 字段顺序、类型、别名必须严格一致,否则
UNION报错ERROR 1222
更安全的写法:用两个 LEFT JOIN 替代 RIGHT JOIN
有些人不熟悉 RIGHT JOIN 语义,容易写反表顺序。换成两次 LEFT JOIN 更直观,且兼容性更好(某些旧版 MySQL 对 RIGHT JOIN 解析不稳定)。
SELECT a.id, a.name, b.order_id, b.amount FROM users a LEFT JOIN orders b ON a.id = b.user_id UNION ALL SELECT NULL AS id, NULL AS name, b.order_id, b.amount FROM orders b LEFT JOIN users a ON a.id = b.user_id WHERE a.id IS NULL;
说明:
- 第二段把
orders当主表,LEFT JOIN users后筛选a.id IS NULL,逻辑更直白 - 显式用
NULL AS id确保字段类型和长度对齐(比如id是INT,不能只写NULL让 MySQL 推断) - 若
users.name是VARCHAR(50),第二段也要写NULL AS name,避免隐式转换失败
性能与可维护性提醒
这种模拟方式在大数据量下比原生 FULL OUTER JOIN 慢——因为要扫描右表两次(一次 join,一次 where 过滤),且无法利用复合索引优化 WHERE a.id IS NULL。
- 务必给关联字段建索引:
orders(user_id)和users(id)都要有 - 如果只是偶尔查、数据量小(FULL OUTER JOIN 的数据库
- 别在子查询里套这个结构,MySQL 5.7 及以前对
UNION子查询优化差,可能全表扫描 - 字段多时容易漏写
NULL占位,建议先用SELECT *写通逻辑,再逐个补类型
最常被忽略的是字段对齐和 NULL 显式声明——看着语句跑通了,导出 CSV 时某列突然变空字符串或 0,大概率是这里没写对。











