pg_dump默认不识别复合主键,导致导出时主键字段丢失或重复;需手动检查DDL、分段导出或使用--dependency确保依赖顺序,并注意FOREIGN_KEY_CHECKS设置及SQLite到PG的约束语法转换。
导出时主键字段丢失或重复,pg_dump 默认不识别复合主键
postgresql 的 pg_dump 不会主动把 primary key (a, b) 当作一个逻辑单元处理;它只按单列索引或约束名提取,导出的 create table 语句里可能漏掉 primary key (col1, col2),只剩 unique 或干脆没约束。结果是还原后表结构“看起来一样”,但外键引用、orm 映射、迁移工具识别全乱套。
实操建议:
- 用
pg_dump --schema-only导出 DDL,再手动检查CREATE TABLE语句末尾是否有PRIMARY KEY (col1, col2)—— 没有就说明约束被拆成独立UNIQUE或遗漏了 - 优先用
pg_dump --inserts --column-inserts而非--inserts:后者生成无列名的INSERT,一旦表结构字段顺序微调(比如加了个中间字段),数据就插错列 - 若表带
GENERATED ALWAYS AS IDENTITY列,必须加--inserts,否则pg_restore无法跳过该列赋值
外键指向复合主键表,pg_dump 默认不保证依赖顺序
当表 orders 的外键指向 customers (country_code, id) 这种复合主键时,pg_dump 默认按字母序 dump 表,可能先 dump orders 再 dump customers。还原时报错:ERROR: relation "customers" does not exist,哪怕你用了 --disable-triggers 也拦不住建表阶段的依赖失败。
实操建议:
- 加
--section=pre-data+--section=data+--section=post-data分段导出,手动调整pre-data中建表顺序:把被引用的复合主键表(如customers)放在引用表(如orders)之前 - 更稳的做法是用
pg_dump --dependency(需 15+ 版本),它会基于 pg_depend 生成拓扑排序后的 SQL,自动处理复合主键表的前置依赖 - 别信
--clean和--if-exists能绕过顺序问题——它们只影响 DROP,不改变 CREATE 的执行次序
用 mysqldump 导出含复合主键的 InnoDB 表,FOREIGN_KEY_CHECKS 关不干净
MySQL 的 mysqldump 在导出含外键的复合主键表时,会在文件开头写 SET FOREIGN_KEY_CHECKS = 0;,但如果你用 --skip-triggers 或中途修改过 dump 文件,这个开关可能被注释掉、漏写,或被目标库的 init_connect 覆盖。还原时外键校验开着,而数据插入顺序又不对,直接卡在第一条 INSERT。
实操建议:
- 导出后立刻 grep 验证:
grep "FOREIGN_KEY_CHECKS" dump.sql,确认第一行是SET FOREIGN_KEY_CHECKS = 0;,最后一行是SET FOREIGN_KEY_CHECKS = 1; - 避免用
--no-create-info单独导数据:它不带SET FOREIGN_KEY_CHECKS,必须自己补 - InnoDB 表若定义了
ON DELETE CASCADE指向复合主键,dump 出的CREATE TABLE语句里FOREIGN KEY子句必须显式写出所有列,例如FOREIGN KEY (c_code, c_id) REFERENCES customers (country_code, id);少一列就会变成无效外键
从 SQLite 导出复合主键表到 PostgreSQL,sqlite3 .dump 不保留主键语义
SQLite 的 .dump 命令输出的是“能跑就行”的 SQL,对 PRIMARY KEY (a, b) 只生成 PRIMARY KEY(a,b) 字样,但 PostgreSQL 解析时会报错:syntax error at or near "(" —— 因为 PG 要求复合主键必须写成 CONSTRAINT pk_name PRIMARY KEY (a, b),不能省略 CONSTRAINT 名。
实操建议:
- 别直接 pipe
sqlite3 db.sqlite ".dump"到 psql;先用脚本把PRIMARY KEY (col1, col2)替换成CONSTRAINT pk_table PRIMARY KEY (col1, col2),注意括号前后空格和换行要匹配 - SQLite 的
WITHOUT ROWID表如果以复合列为 PK,dump 出来没有rowid,但 PG 没对应机制,必须加个id SERIAL PRIMARY KEY并重写所有 INSERT - 用
csvsql(from csvkit)比手写转换更可靠:它读 SQLite schema 后生成符合目标库语法的 DDL,自动处理复合主键命名和约束位置
复合主键不是“多列标了主键”就完事——导出工具看到的是约束定义、依赖图、SQL 解析规则三者的交集。少查一行 pg_constraint 查询,或者漏看一眼 dump 文件里的 SET 语句,还原时就卡在第一个 INSERT。










