MySQL不支持FULL OUTER JOIN,需用LEFT JOIN+RIGHT JOIN+UNION ALL组合实现:先取左表全量及匹配右表数据,再补右表独有行(WHERE左表ID IS NULL),必须用UNION ALL避免去重误删,且字段名、类型、顺序须严格对齐。

说个直白的现实:MySQL里没有FULL OUTER JOIN,你直接写上去,数据库会甩你一个ERROR 1064。而PostgreSQL、SQL Server、Oracle倒是原生支持,但用的不好,NULL处理和连接键的质量会直接让结果变成废纸。今天就把这事拆开揉碎了说清楚。
MySQL 怎么写等效的 Full Outer Join
标准套路是用LEFT JOIN + RIGHT JOIN + UNION ALL拼出来。但有两个坑:一是两个子查询里都必须加上WHERE过滤条件,否则交集行会重复,NULL键也匹配不上;二是必须用UNION ALL,千万别手滑写成UNION——后者会去重,而左右两边那些结构相同的NULL行会被误删,数据就丢了。
LEFT JOIN部分负责保留左表全部,右表不匹配的字段自动填NULLRIGHT JOIN部分只取“右表有、左表无”的行,别忘了加WHERE left_table.id IS NULL- 列名要显式写出来,别用
*,字段顺序或别名一冲突,UNION直接罢工
举个例子,合并用户表users和订单表orders:
SELECT u.id, u.name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id UNION ALL SELECT u.id, u.name, o.order_id, o.amount FROM users u RIGHT JOIN orders o ON u.id = o.user_id WHERE u.id IS NULL;
PostgreSQL 中 FULL OUTER JOIN 的 NULL 陷阱
语法没问题,但结果里大量的NULL是最容易踩的坑。比如你顺手加了个WHERE amount > 100,所有右表独有的行(amount是NULL)全被干掉了——而你本意可能是“金额超100,或者该用户根本没下单”。
WHERE条件里涉及任一表的字段,都必须考虑IS NULL分支- 连接键本身如果允许
NULL,那FULL OUTER JOIN会变成笛卡尔积冲击波:两表各有3条id IS NULL的记录,一连接就产出9行 - 安全做法是在
ON子句里提前排除:ON u.id = o.user_id AND u.id IS NOT NULL AND o.user_id IS NOT NULL - 主键字段一般不会为
NULL,但业务字段(如region_code)常有空值,得提前用COALESCE归一化,或者清洗后再连
跨系统基础数据合并时最关键的三件事
别以为语法对了就万事大吉。两个系统的原始数据结构不同、主键定义不一致、空值含义不统一,直接硬连,出来的数据能骗死人。
- 先确认“匹配依据”是不是真能唯一对应。系统A用手机号,系统B用邮箱,中间没做映射就硬连,结果全是
NULL,查都查不明白 - 字段类型要对齐。
INT和VARCHAR连不上;时间字段一个存UTC一个存本地时区,JOIN条件直接失效 - 业务上“空”不等于“不存在”。系统A的
status = NULL表示待审核,系统B的status = NULL表示已注销——合并后简单填个COALESCE(a.status, b.status),轻则逻辑错乱,重则业务事故
说到底,真正难的从来不是写对那一行FULL OUTER JOIN,而是搞清楚两边字段背后的真实业务语义。空值怎么解释,缺失代表什么,要不要补默认值——这些不厘清,SQL写得再漂亮,结果也是误导。
