从一个表复制到另一个表,如何对整个数据集而不是单独的行强制执行外键检查?

Posted

技术标签:

【中文标题】从一个表复制到另一个表,如何对整个数据集而不是单独的行强制执行外键检查?【英文标题】:Copying from one table to other, how to enforce foreign key check on the whole data set but not on separate rows? 【发布时间】:2018-03-06 16:13:05 【问题描述】:

我正在使用 mysql。假设我有一个表 hierarchy 有两列:idparent_id

parent_id 指的是同一张表的另一行的id,所以我那里有外键。

hierarchy 表包含一些数据,但它们现在不相关。

我还有另一个名为 new_hierarchy_entries 的表具有相同的列,但没有设置外键限制。

new_hierarchy_entries 包含:

id    parent_id
2     1
1     null

现在我想将所有行从new_hierarchy_entries 复制到hierarchy。当我天真地奔跑时:

INSERT INTO hierarchy SELECT * FROM new_hierarchy_entries

我收到错误:无法添加或更新子行:外键约束失败(my_db.hierarchy, CONSTRAINT hierarchy_ibfk_2 FOREIGN KEY (parent_id) REFERENCES hierarchy (id))

当然,如果逐行插入,第一行(id=2,parent=1)就无法插入,因为表hierarchy中没有id=1的行。

另一方面,如果一次添加所有行,则将满足约束条件。那么如何复制行以确保在复制后满足约束条件,但在复制时可能不满足约束条件?

idnew_hierarchy_entries 的行进行排序将无济于事。我不能假设 parent_id < id 在同一行。

按层次结构对new_hierarchy_entries 的行进行排序(使用树术语,先给我叶子,然后给我他们的父母等)会有所帮助,但我不确定如何在 MySQL 查询中做到这一点。

我的想法是暂时关闭 FOREIGN_KEY_CHECKS。但后来我可以插入不一致的数据,但我不会发现。打开 FOREIGN_KEY_CHECKS 不会使数据库检查所有数据的一致性。反正会占用太多资源。

【问题讨论】:

【参考方案1】:

这很棘手。在启用 FOREIGN_KEY_CHECKS 后,我不知道有什么方法可以让 MySQL 重新检查外键引用。

您可以检查自己是否有孤立行,如果有,请回滚。

BEGIN;

SET SESSION FOREIGN_KEY_CHECKS=0;
INSERT INTO hierarchy SELECT * FROM new_hierarchy_entries;
SET SESSION FOREIGN_KEY_CHECKS=1;

SELECT COUNT(*) FROM hierarchy AS c 
LEFT OUTER JOIN hierarchy AS p ON p.id=c.parent_id
WHERE p.id IS NULL;
-- if count == 0 then...
COMMIT;

-- otherwise ROLLBACK and investigate the bad data

另一种可能性是将 INSERT 与 IGNORE 选项一起使用,这将跳过失败的行。然后在循环中重复相同的语句,只要您看到“受影响的行数”超过 0。

INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
...

【讨论】:

以上是关于从一个表复制到另一个表,如何对整个数据集而不是单独的行强制执行外键检查?的主要内容,如果未能解决你的问题,请参考以下文章

将过滤后的数据从一个工作表复制到另一个工作表

将多个表(或整个模式)从一个集群复制到另一个集群

如何将数据从一个 BigTable 表复制到另一个 BigTable 表

oracle中如何将数据从一个表复制到另一个表(仅复制其中某些字段)

在 SQL 中将行从一个表复制到另一个表

如何使用复制命令在 postgres 中将数据从一个表复制到另一个表