必须关闭外键约束的三种情况:批量导入无序数据、修复悬挂外键、跨表结构重组;关闭后须立即确认状态为0、导入后即刻设回1、并手动校验孤儿记录。

导入大量历史数据时遇到“Cannot add or update a child row”报错,本质是外键约束在拦截非法引用;临时关闭外键检查能绕过验证,但必须明确知道何时关、何时开、关了之后要做什么。
什么情况下必须关闭外键约束
方法一:批量导入无序数据(如从旧库导出的SQL文件)
当导入脚本中 INSERT 语句顺序与表依赖关系不一致时——比如先插 orders 表再插 users 表——MySQL 会因外键校验失败而中断执行。【此时必须先 SET FOREIGN_KEY_CHECKS = 0; 再导入】
方法二:修复已存在的悬挂外键
发现某张表里存在 user_id=999 的订单,但 users 表中根本没 id=999 的用户,这种数据已违规。直接 DELETE 或 UPDATE 会被外键阻止,只能先关约束再修正。
方法三:执行跨表结构重组
比如要把 users.id 拆成 users.uuid 并让 orders.user_id 改为引用 uuid 字段。中间阶段必然存在新旧字段并存、约束未就绪的状态,需暂时禁用约束以完成迁移。
关闭后必须立即执行的三件事
第一步:确认当前会话已生效
执行 SELECT @@FOREIGN_KEY_CHECKS; 返回值应为 0。若仍为 1,说明 SET 命令未执行成功或被事务回滚覆盖。
第二步:导入/修改完成后,立刻重新启用约束
执行 SET FOREIGN_KEY_CHECKS = 1; 这一步不能省略,也不能等到明天再做——【只要会话还开着,约束就一直处于关闭状态】,后续所有写入都失去参照完整性保护。
第三步:手动校验外键一致性
启用后立即运行类似 SELECT o.id FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.id IS NULL; 查找所有悬挂记录。有结果就必须人工清理或补数据,否则下次开启约束时虽不报错,但数据已不可信。
哪些操作不能靠关约束来解决
方法一:父表主键类型与子表外键类型不匹配
例如 users.id 是 BIGINT,orders.user_id 却定义为 INT——即使关了约束,ALTER TABLE 修改列类型时仍会失败,因为底层索引和存储结构冲突。必须先改子表字段类型,再操作数据。
方法二:试图删除被引用的父表记录且未配置级联
SET FOREIGN_KEY_CHECKS = 0 后执行 DELETE FROM users WHERE id = 123; 确实能删掉,但 orders 表里所有 user_id = 123 的行就成了孤儿。这不是“解决问题”,是制造隐患。正确做法是加 ON DELETE CASCADE 或先删子表再删父表。
方法三:MyISAM 表上使用外键约束
MyISAM 存储引擎根本不支持外键,SET FOREIGN_KEY_CHECKS 对它完全无效。看到报错提示“ERROR 1005”或“Can't create table”时,先查 ENGINE 类型,别浪费时间调约束开关。

















