导出多表需按外键依赖顺序:先父表后子表,MySQL用INFORMATION_SCHEMA查依赖并拓扑排序,mysqldump加--skip-triggers和--skip-extended-insert;PostgreSQL用pg_dump分section导出;SQLite用PRAGMA foreign_keys=OFF临时禁用约束。
导出前必须先理清外键依赖链
mysql 或 postgresql 导出多张有外键约束的表时,如果顺序错乱(比如先导出子表再导出父表),后续导入会失败,报错类似 error 1217 (hy000): cannot delete or update a parent row: a foreign key constraint fails。这不是导出工具的问题,而是数据完整性机制在起作用。
实操建议:
- 用
SELECT TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'your_db' AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY REFERENCED_TABLE_NAME;查出所有外键引用关系 - 手动画出依赖图:箭头从子表指向父表(如
orders → users),拓扑排序后得到安全导出顺序 - 别依赖“按字母序导出”——
products可能依赖categories,但字母上categories在前,实际要先导categories
mysqldump 要加 --skip-triggers --skip-extended-insert 才好调序
mysqldump 默认导出含 CREATE TABLE 和 INSERT,但触发器、批量插入语句会让人工调整表顺序变得困难——你改了 INSERT 顺序,触发器可能又偷偷写入新记录,破坏一致性。
实操建议:
- 导出单表时固定用:
mysqldump -u user -p --no-create-info --skip-triggers --skip-extended-insert your_db table_name > table_name.sql -
--no-create-info省掉建表语句,方便你统一管理 DDL;--skip-extended-insert让每行INSERT独立成句,便于剪切/重排 - 导出完所有表后,自己按依赖顺序拼接 SQL 文件,再补上统一的
CREATE TABLE(注意外键定义里ON DELETE CASCADE这类行为也要对齐)
PostgreSQL 用 pg_dump --section=pre-data 分离结构与数据
PostgreSQL 的 pg_dump 比 MySQL 更适合分阶段导出,它原生支持按逻辑阶段拆分输出:pre-data(含建表+外键)、data(纯 INSERT)、post-data(索引、约束等)。这样你就能先导 pre-data,手动删掉外键定义,等所有表数据导完再补上——绕过导入时的顺序卡点。
实操建议:
- 先导结构(不含外键):
pg_dump -U user -d your_db --section=pre-data --no-tablespaces --no-owner --no-privileges > schema_no_fk.sql,然后手动删掉文件里所有FOREIGN KEY行 - 再导数据:
pg_dump -U user -d your_db --section=data --no-tablespaces --no-owner --no-privileges > data.sql - 最后补外键:
pg_dump -U user -d your_db --section=post-data --no-tablespaces --no-owner --no-privileges | grep "FOREIGN KEY" >> add_fk.sql
SQLite 没有服务端,靠 PRAGMA foreign_keys = OFF 临时关约束
SQLite 是嵌入式数据库,不支持像 MySQL 那样用 SET FOREIGN_KEY_CHECKS = 0。但它提供运行时开关:PRAGMA foreign_keys。只要在导入前关掉,导入后再打开,就能无视顺序直接灌数据。
实操建议:
- 导出仍按依赖顺序做,但导入脚本开头加:
PRAGMA foreign_keys = OFF;,结尾加:PRAGMA foreign_keys = ON; - 注意:关闭后不会校验外键,所以确保你的数据本身是干净的——比如
user_id在orders表里不能出现users表中不存在的值 - 别在生产环境长期关这个,只用于迁移场景;SQLite 命令行导入时记得用
.read your_dump.sql,否则PRAGMA不生效
真正麻烦的不是怎么导,而是导完发现某张中间表漏了依赖没查到,或者外键定义里用了未导出的自定义函数(比如 MySQL 的 GENERATED ALWAYS AS 列带函数)。这种细节没法靠工具自动识别,得对着 INFORMATION_SCHEMA 或 pg_constraint 一行行核对。











