mysql不支持full outer join,需用left join+right join+union all模拟:先取左表全量及匹配右表数据,再补右表独有行(where左表id is null),避免重复和null键遗漏。

直接结论:用 LEFT JOIN + RIGHT JOIN + UNION ALL 比 FULL JOIN 更稳,尤其当两个环境数据库类型不一致(比如一边 SQL Server、一边 MySQL)时,FULL JOIN 语法可能直接报错。
为什么不能直接写 FULL JOIN?
SQL Server 和 PostgreSQL 支持 FULL OUTER JOIN,但 MySQL 从 8.0 到最新版(截至 2026 年)仍不支持该语法,硬写会触发 ERROR 1054 (42S22): Unknown column 或更直白的语法错误。如果你要对比的是“生产库(SQL Server)”和“测试库(MySQL)”,那连建视图都过不了第一关。
即使同是 SQL Server,若表里主键字段允许 NULL(比如 id 是 INT NULL),WHERE a.id IS NULL 就会误判——NULL 值本身不是“缺失”,而是合法数据。所以连接键必须是 NOT NULL 的业务主键(如 config_key),且类型一致。
- 检查主键定义:
SELECT COLUMN_NAME, IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'config' AND COLUMN_NAME = 'config_key' - 类型不一致时(如一边是
VARCHAR(50),一边是NVARCHAR(50)),JOIN 可能静默失败或漏匹配,建议统一用CAST(config_key AS VARCHAR(50))
LEFT + RIGHT + UNION ALL 的实操写法
这是跨库、跨版本最兼容的写法,不依赖数据库特性,结果可预测。关键点是显式补 NULL、加来源标记、避免 UNION 去重。
假设你要比的表叫 config,主键是 config_key,两环境分别连为 prod.config 和 test.config(实际执行前需确保能跨库访问,或先导出为本地临时表):
SELECT 'only_in_prod' AS source, p.config_key, p.value AS value_prod, NULL AS value_test FROM prod.config p LEFT JOIN test.config t ON p.config_key = t.config_key WHERE t.config_key IS NULL <p>UNION ALL</p><p>SELECT 'only_in_test' AS source, t.config_key, NULL AS value_prod, t.value AS value_test FROM prod.config p RIGHT JOIN test.config t ON p.config_key = t.config_key WHERE p.config_key IS NULL;</p>
- 必须用
UNION ALL:UNION会去重,而配置重复本身就是异常信号 - 所有字段必须一一对应:左半部分
value_prod有值、value_test补NULL;右半部分反过来 - 别省略
source字段:后续排查时靠它快速定位是哪个环境缺配置
查“同 key 但 value 不同”的行,别只盯 NULL
上面的写法只能抓出“单边缺失”,但配置差异更多发生在“两边都有、值却不同”——比如生产环境 timeout=30,测试环境还是旧的 timeout=15。这时得用 INNER JOIN 配合字段比对:
SELECT 'value_mismatch' AS source, p.config_key, p.value AS value_prod, t.value AS value_test FROM prod.config p INNER JOIN test.config t ON p.config_key = t.config_key WHERE COALESCE(p.value, '') != COALESCE(t.value, ''); -- 处理 NULL 值比较
-
COALESCE是必须的:直接写p.value != t.value在任一字段为 NULL 时整个条件返回 UNKNOWN,查不到结果 - 字符串比较注意空格:MySQL 默认忽略末尾空格,SQL Server 区分,建议加
TRIM()或统一用RTRIM(LTRIM()) - 如果 value 是 JSON 或大文本,别在 WHERE 里直接比,先加哈希字段(如
HASHBYTES('SHA2_256', value))再比哈希值,性能高一个数量级
容易被忽略的三个落地细节
很多人写完 SQL 能跑通,但上线后发现漏差、慢、或结果不可信——问题往往不在 JOIN 逻辑本身,而在环境准备和字段处理上。
- 时间字段没过滤:配置表常带
updated_at,如果只比全量,一次拉几百万行,JOIN 直接卡死。加WHERE p.updated_at > DATEADD(day, -7, GETDATE())(SQL Server)或WHERE p.updated_at > NOW() - INTERVAL 7 DAY(MySQL)限定范围 - 大小写敏感没对齐:SQL Server 默认不区分大小写,MySQL 取决于 collation。若
prod用utf8mb4_0900_as_cs(区分大小写),test用utf8mb4_general_ci(不区分),'API_URL'和'api_url'就会被当成相同 key。比对前统一转小写:LOWER(p.config_key) - 没验证连接权限:跨库 JOIN 要求账号对两个库都有
SELECT权限,且网络可达。常见错误是 SSMS 里能连生产库、但连不上测试库,报错Login failed for user或超时,却误以为 SQL 有问题











