[英]Deleting a row with a self-referencing foreign key
我有一个 MySQL 表,其定义如下:
CREATE TABLE `guestbook` ( `Id` int(10) unsigned NOT NULL, `ThreadId` int(10) unsigned NOT NULL, PRIMARY KEY (`Id`), KEY `ThreadId` (`ThreadId`), CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ) ENGINE=InnoDB;
目前表中只有 1 行:
mysql> select * from guestbook; +-----+----------+ | Id | ThreadId | +-----+----------+ | 211 | 211 | +-----+----------+
问题是没有办法在不破坏约束的情况下删除这一行。
mysql> delete from guestBook; ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`polaris`.`guestbook`, CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`))
由于 ThreadId 列被定义为非空,因此也无法临时将 ThreadId 设置为不同的值来删除该行。 有没有办法删除行而不更改表的定义或删除整个表?
您可以使用此查询暂时禁用外键约束:
SET foreign_key_checks = 0;
如果您对外键执行ON DELETE CASCADE
操作,您应该能够删除自引用的行。
CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ON DELETE CASCADE
与使用ON DELETE SET NULL
,这样做的好处是您不必更改架构以使“ThreadId”列可以为空。
有几种解决方法。 其他人建议的方法......
SET foreign_key_checks = 0;
...将禁用每个表的外键。 这不适合在共享环境中使用。
另一种方法是使用删除外键
ALTER TABLE `guestbook`
DROP FOREIGN KEY `guestbook_ibfk_1`
/
我们可以使用 DML 整理数据,然后使用以下方法恢复外键:
ALTER TABLE `guestbook`
ADD CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`)
REFERENCES `guestbook` (`Id`)
/
但是有没有办法在不执行任何 DDL 的情况下更改数据? 好吧,我们可以插入一条新记录并更改当前记录以引用它:
INSERT INTO `guestbook` VALUES (212, 211)
/
UPDATE `guestbook`
SET `ThreadId` = 212
WHERE `Id` = 211
/
精明的观察者会注意到,我们最终仍然是相互依赖,只是在记录之间。 所以我们还没有真正进步; 我们现在有两个无法删除的记录,而不是一个。 (顺便说一句,这适用于我们在删除或禁用外键时可能执行的任何 DML)。 所以,也许我们需要重新考虑数据模型。 我们是对具有循环依赖关系还是层次结构的图进行建模?
一个分层的数据结构至少需要一个根节点,一个其他记录可以依赖的记录,但它本身不依赖任何记录。 实现这一点的常用方法是使外键列可选。 在层次结构的最上层,记录在该列中必须为 NULL。 是否应该只有一个这样的根节点或是否允许多个根节点取决于您的业务规则。
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
在建模方面,这与作为它自己的母版的记录没有什么不同,但它是一种更直观的解决方案。
无法删除自引用行是MySQL 中一个长期存在的已知错误/未完成的功能请求。
在您遇到此问题的许多情况下,您可以在执行删除之前将外键设为 NULL,因此您的解决方法只会影响您想要的行(使用相同的 WHERE 子句)。
如果你在我的外键上设置了一个ON DELETE SET NULL
,它让我删除一个自引用。 如果我没有指定ON DELETE
,MySQL 默认为RESTRICT
。
当然,请确保该列是NULLABLE
。 您也可以根据默认值尝试SET DEFAULT
。 但请记住, NO ACTION
只是 MySQL 中RESTRICT
的别名!
仅在 MySQL 5.6 上测试(最初发布此问题时未发布)。
雅暂时禁用外键
set foreign_key_checks=0;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.