SQL Server中必须显式使用FULL OUTER JOIN而非FULL JOIN才能实现双向结构比对,它能一次性捕获两库间表与字段的独有及差异项,是结构审计最高效起点。

SQL Server里FULL JOIN是唯一能一次性拉出两边“独有结构”的方式
SQL Server原生不支持FULL OUTER JOIN的简写FULL JOIN,但必须写全FULL OUTER JOIN才能生效。它不像LEFT JOIN或INNER JOIN那样只关注一侧,而是把两个库中所有表、所有字段都摊开比——哪怕某张表只在主库存在、某列只在备库定义,也能一并捕获。这是做结构审计最省力的起点。
常见错误现象:FULL JOIN被误写成FULL JOIN(缺OUTER),SQL Server直接报错Incorrect syntax near 'JOIN';或者用LEFT JOIN + RIGHT JOIN + UNION ALL手动模拟,结果漏掉两边都为NULL的边界情况。
- 必须显式写
FULL OUTER JOIN,不能省略OUTER - 连接条件要同时匹配
table_name和column_name,否则会把不同表的同名列错误对齐 - 对比前务必统一两库的
COLLATE,否则大小写敏感差异会导致ON条件失效
用sys.columns + FULL OUTER JOIN提取字段级差异
直接查sys.columns比用INFORMATION_SCHEMA.COLUMNS更可靠:前者包含is_identity、is_nullable、max_length等底层属性,后者在跨版本或某些兼容模式下会丢精度(比如varchar(max)显示为-1)。
关键点在于把主库和备库的字段元数据分别存入临时表,再用FULL OUTER JOIN对齐:
SELECT ISNULL(t1.table_name, t2.table_name) AS table_name, ISNULL(t1.column_name, t2.column_name) AS column_name, t1.system_type_id AS src_type_id, t2.system_type_id AS dst_type_id, t1.max_length AS src_maxlen, t2.max_length AS dst_maxlen, t1.is_nullable AS src_null, t2.is_nullable AS dst_null FROM #src_cols t1 FULL OUTER JOIN #dst_cols t2 ON t1.table_name = t2.table_name AND t1.column_name = t2.column_name WHERE t1.table_name IS NULL OR t2.table_name IS NULL OR t1.system_type_id != t2.system_type_id OR t1.max_length != t2.max_length OR t1.is_nullable != t2.is_nullable;
-
ISNULL(t1.table_name, t2.table_name)确保缺失表也能显示名称 -
WHERE子句里用IS NULL抓独有表,用!=抓类型/长度/空值性差异 - 别忘了
user_type_id——自定义类型(如dbo.phone_number)和系统类型(如varchar)的system_type_id可能相同,必须连user_type_id一起比
MySQL没法直接用FULL OUTER JOIN?那就用UNION ALL模拟
MySQL直到8.0.28才支持FULL OUTER JOIN,老版本(尤其是线上大量5.7环境)必须手写等效逻辑。核心思路是:用LEFT JOIN找出主库有、备库无的字段;用RIGHT JOIN反向抓备库独有字段;再用INNER JOIN比对共有的字段差异——三者UNION ALL拼起来。
容易踩的坑:
- 两次
JOIN的ON条件必须完全一致,否则同一字段可能被重复计入 -
UNION ALL前各子查询的列数、顺序、类型必须严格一致,否则报错Column count doesn't match - MySQL的
INFORMATION_SCHEMA.COLUMNS里DATA_TYPE是字符串,CHARACTER_MAXIMUM_LENGTH对text类返回NULL,比对前得先COALESCE归一化
生成可执行的ALTER语句前,先验证字段变更是否安全
FULL OUTER JOIN输出的是“差异快照”,不是“操作指令”。比如发现备库少一个NOT NULL字段,直接ADD COLUMN可能失败——如果表已有数据,SQL Server要求带DEFAULT或允许NULL。
真正落地时,必须结合业务约束判断:
- 字段类型从
int扩到bigint可安全ALTER,但从varchar(50)缩到varchar(20)需先CHECK现有数据长度 - 主键/索引变更必须停写,否则
ALTER TABLE ... DROP CONSTRAINT可能阻塞业务 - 生产环境严禁用
mysqldiff --difftype=sql生成的脚本直接跑,它不校验外键依赖或触发器影响
最常被忽略的一点:FULL OUTER JOIN本身不区分“主库是源”还是“备库是源”,但生成ALTER语句时,你必须明确以哪边为基准——这个方向选错,整个同步逻辑就反了。


















