SQLAlchemy反向生成表结构比对最稳,因其通过MetaData.reflect()统一抽象字段为Python对象,兼容跨库类型映射(如MySQL INT(11)与PostgreSQL INTEGER均映射为Integer),并支持精准比对字段名、类型类、nullable、default等关键属性,避免str(col.type)含长度导致的误判,且可选语义比对(忽略顺序)或结构比对(按序逐项)。

用 sqlalchemy 反向生成表结构再比对最稳
直接连库查 information_schema 虽快,但跨数据库(比如 MySQL vs PostgreSQL)字段类型映射不一致,容易把 INT(11) 和 INTEGER 当成不同类型。用 sqlalchemy 的 MetaData.reflect() 统一抽象成 Python 对象,再比对字段名、类型、是否为空、默认值等关键属性,兼容性好、逻辑清晰。
实操建议:
- 两个数据库分别建
Engine,用相同版本的sqlalchemy(推荐 2.0+),避免反射行为差异 - 只反射目标表(传
tables=[...]),别全库反射,尤其生产库表多时太慢 - 字段类型统一用
col.type.__class__比较(如StringvsText),别用str(col.type),后者含长度参数,易误判 - 注意
nullable:MySQL 默认NULL,PostgreSQL 默认NOT NULL,空值约束必须显式比对
inspect.get_columns() 比 reflect() 更轻量但少信息
如果只要字段名、类型、是否主键,不关心索引或外键,sqlalchemy.inspect(engine).get_columns(table_name) 更快,不触发完整元数据加载。但它返回的是字典列表,没有原生类型对象,type 字段是字符串(如 'VARCHAR'),跨库比对仍需手动归一化。
常见坑:
立即学习“Python免费学习笔记(深入)”;
-
get_columns()不返回default或server_default,要对比默认值得换方式 - PostgreSQL 的
serial类型会返回'INTEGER',但实际是自增,需额外查pg_get_serial_sequence补充判断 - MySQL 8.0+ 的
JSON类型在旧版sqlalchemy里可能被识别为TEXT,版本不匹配就漏差
字段顺序不同但内容一致,算不算差异?
多数 DBA 关注字段语义是否一致,不关心顺序——毕竟 SELECT * 本就不该依赖顺序。但有些 ORM(如 Django)生成迁移时会按顺序建表,顺序错会导致迁移失败。所以比对逻辑得支持两种模式:
- 语义比对(推荐):按字段名哈希分组,忽略顺序,只报
name、type、nullable、default四项不一致 - 结构比对:先按
ordinal_position排序再逐项比,适合生成 DDL 差异脚本 - 用
set直接比字段名集合,能快速发现“有表A有、表B没有”的缺失字段,比循环快得多
别忘了 collation、comment、autoincrement 这些隐藏差异
字段名和类型一样,不代表真一致。MySQL 的 collation='utf8mb4_0900_as_cs' 和 'utf8mb4_unicode_ci' 行为不同;PostgreSQL 的 COMMENT 常存业务含义;autoincrement 在 SQLite 和 MySQL 里含义也不同。这些不显眼的配置,上线后可能引发排序错误或文档丢失。
实操要点:
-
get_columns()返回的comment字段在部分方言里是None,得用方言专属 SQL 查(如 MySQL 用SHOW FULL COLUMNS) -
autoincrement别只看布尔值,SQLite 的INTEGER PRIMARY KEY自动autoincrement=True,但 MySQL 需显式写AUTO_INCREMENT - 字符集和校对规则在
create_engine()时传connect_args={'charset': 'utf8mb4'}无法影响已有表,得单独查系统表
真正难的不是找出差异,而是判断哪些差异会影响业务逻辑——比如 VARCHAR(255) 改成 VARCHAR(191) 在 MySQL + utf8mb4 下刚好卡住索引长度限制,这种得结合目标库版本和字符集一起验。


















