Navicat迁移Oracle至PostgreSQL需手动修正类型映射、时区处理、大小写、主键及索引:NUMBER统一映射为int4/int8/float8;DATE用timestamp without time zone;VARCHAR2改text;批量转小写标识符;补建主键、序列及索引。
Navicat 的类型映射必须手动改,不能信默认值
navicat 对 oracle → postgresql 的字段类型转换是“有策略但不智能”——它会把 number 一律映射为 numeric(1000,53),这不是 bug,是它保守的兜底逻辑。但这个默认值在真实业务中基本不可用:存储膨胀、python 读取报 overflowerror、join 性能掉 30%+ 都是常见后果。
你必须在「数据传输」弹窗打开后,点击「高级选项 → 字段映射 → Edit Type Mapping」进去逐条覆盖。重点改这三类:
-
NUMBER(10,0)→ 改为int4(不是integer,避免某些版本解析歧义) -
NUMBER(*,0)(无精度声明的整数)→ 改为int8(对应 Python 的int,防超限) -
NUMBER(x,y)(带小数)→ 改为float8(不是numeric(x,y),除非你真需要银行级精度)
DATE 和 VARCHAR2 的坑比 NUMBER 还隐蔽
DATE 在 Oracle 里实际包含时分秒,但没时区信息;PostgreSQL 的 timestamp with time zone 会强制按当前 session 时区解释,导致迁移后时间偏移。别选带 with time zone 的选项,统一用 timestamp without time zone,后续再按业务规则补时区逻辑。
VARCHAR2(4000) 默认被映射成 character varying(4000),看着没问题,但遇到 emoji 或 UTF-8 四字节字符时会截断。直接改成 text,PostgreSQL 对 text 的处理更宽松,且无长度限制,兼容性远高于固定长度 varchar。
迁移后字段名大小写不一致,SQL 就全挂
Oracle 默认大写标识符("USER_ID"),PostgreSQL 默认小写(user_id)。Navicat 迁移完的表和字段全带双引号+大写,结果你原来写的 SELECT user_id FROM users 直接报错:column "user_id" does not exist。
别手动一个一个改,用这两条 SQL 批量修复:
SELECT 'ALTER TABLE "' || tablename || '" RENAME TO ' || lower(tablename) || ';' FROM pg_tables WHERE schemaname = 'public';
SELECT 'ALTER TABLE ' || table_name || ' RENAME "' || column_name || '" TO ' || lower(column_name) || ';' FROM information_schema.columns WHERE table_schema = 'public';
执行完再 psql 里 \d 表名 确认字段已变成小写无引号。
自增主键和索引得自己补,Navicat 一个都不建
Navicat 的「数据传输」只建空表+插数据,PRIMARY KEY、UNIQUE、INDEX、SEQUENCE 全部丢弃。如果你漏了这步,后续插入会失败,查询没索引变慢十倍,应用层还可能因主键缺失报 IntegrityError。
先重建主键(假设原 Oracle 主键叫 ID):
ALTER TABLE users ADD PRIMARY KEY (id);
再建序列并绑定自增:
CREATE SEQUENCE users_id_seq START WITH 1 INCREMENT BY 1;
ALTER TABLE users ALTER COLUMN id SET DEFAULT nextval('users_id_seq');
最后跑一次 SELECT setval('users_id_seq', max(id)) FROM users; 同步当前最大值,否则下次插入会冲突。
真正麻烦的不是操作本身,而是你得从 Oracle 的 USER_CONSTRAINTS 和 USER_INDEXES 里手工捞出所有约束定义,再翻译成 PostgreSQL 语法——这点 Navicat 完全不帮你,也别指望它以后会加。


















