Navicat 15 可比对 PostgreSQL 架构,但必须使用“结构同步”功能且版本不低于 10+;低于该版本会静默跳过分区表、JSONB 字段或生成语法错误的 ALTER 语句。

Navicat 15 能比对 PostgreSQL 架构,但必须用“结构同步”而非“数据对比”,且 PostgreSQL 版本不能低于 10+;低于该版本会静默跳过分区表、JSONB 字段或生成语法错误的 ALTER 语句。
确认连接与版本兼容性
PostgreSQL 小版本差异(如 12.4 ↔ 12.10)通常无碍,但跨大版本(如 11 ↔ 14)可能触发不兼容行为:Navicat 15 不识别 pg_partitioned_table,分区表会被完全忽略;GENERATED ALWAYS AS 列在 12+ 才支持,若源库是 11,Navicat 可能报错或漏建该字段。
- 执行
SELECT version();分别查两个库的实际服务端版本,不是 Navicat 连接信息里显示的“PostgreSQL”字样 - 检查是否启用
track_functions = 'all'(非必需但影响函数比对完整性) - 连接用户需具备
SELECT ON pg_class, pg_attribute, pg_namespace, pg_type等系统表权限,否则“对象选择”页为空或卡在“正在获取元信息”
过滤无效对象并避开分区表陷阱
Navicat 15 的结构同步向导默认扫描全部 schema,但 PostgreSQL 分区表(relkind = 'p')和分区索引(relkind = 'I')不会被识别——这不是配置问题,是工具能力边界。若强行同步,主表 DDL 会丢失 PARTITION BY 子句,子表更不会出现在比对列表中。
- 在“对象选择”页,取消勾选
Partitioned Tables(即使它显示为灰色,也说明 Navicat 根本没查到) - 手动排除含
_p_、_part_等命名特征的子表(如logs_p2024_q1),它们不会参与比对,但你得心里有数 - 对主表,右键 → “对象信息” → 查看 DDL 中是否有
PARTITION BY;若没有,说明逆向工程已失败,需用\d+ table_name在 psql 中人工补全
生成 SQL 前必须调整的三项关键设置
Navicat 默认生成的 PostgreSQL 同步脚本存在三处典型硬伤:外键顺序错乱、JSONB 字段类型误判、ENUM 值新增用 MODIFY COLUMN 导致锁表。
- 点“选项”→ 勾选
Ignore column order和Ignore default value quotes:PostgreSQL 对DEFAULT 'now()'和DEFAULT now()视为不同,但语义等价 - 取消勾选
Generate DROP statements before CREATE:PostgreSQL 的DROP TABLE IF EXISTS在事务中可能引发锁等待,尤其涉及外键依赖链时 - 预览 SQL 后,搜索所有
ALTER TABLE ... MODIFY COLUMN—— PostgreSQL 不支持该语法,必须替换成ALTER TABLE ... ALTER COLUMN ... TYPE或USING表达式转换
执行前务必验证外键与字符集一致性
PostgreSQL 外键比 MySQL 更敏感:两边字段的 collation(排序规则)、character_set(实际由 collation 隐含)、甚至 NOT NULL 属性不一致,都会导致同步后外键失效或比对结果漏项。
- 运行
SELECT conname, pg_get_constraintdef(oid) FROM pg_constraint WHERE contype = 'f' AND conrelid = 'your_table'::regclass;核对两边外键定义是否字面一致 - 查字段 collation:
SELECT column_name, collation_name FROM information_schema.columns WHERE table_name = 't1' AND column_name = 'ref_id';,两边必须完全相同 - 若发现目标库字段是
text COLLATE "C"而源库是text COLLATE "en_US.utf8",Navicat 不会提示,但同步后INSERT可能因 collation 冲突失败
最易被忽略的是:Navicat 从不校验 search_path 是否一致。若源库依赖 public + extensions,而目标库只设了 public,像 jsonb_strip_nulls() 这类函数调用就会在同步后的 DDL 中报错。动手前,先在两个库分别执行 SHOW search_path; 并对齐。











