Sql 删除具有自引用外键的行
我有一个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;Sql 删除具有自引用外键的行,sql,mysql,foreign-keys,Sql,Mysql,Foreign Keys,我有一个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
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
创建表“留言簿”(
`Id`int(10)无符号非空,
`ThreadId`int(10)无符号非空,
主键(`Id`),
关键字'ThreadId'('ThreadId'),
约束“guestbook\u ibfk\u 1”外键(`ThreadId`)引用“guestbook`”(`Id`)
)引擎=InnoDB;
目前表中只有一行:
mysql> select * from guestbook;
+-----+----------+
| Id | ThreadId |
+-----+----------+
| 211 | 211 |
+-----+----------+
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
mysql>从留言簿中选择*;
+-----+----------+
|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`))
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
mysql>从留言簿中删除;
错误1451(23000):无法删除或更新父行:外键约束失败(`polaris`.`guestbook`,约束`guestbook\u ibfk\u 1`外键(`ThreadId`)引用`guestbook`(`Id`)
由于ThreadId列定义为NOTNULL,因此也不可能临时将ThreadId设置为其他值以删除该行。有没有一种方法可以在不更改表的定义或删除整个表的情况下删除行?您可以通过此查询临时禁用外键约束:
SET foreign_key_checks = 0;
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
暂时禁用外键
set foreign_key_checks=0;
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
有几个变通办法。其他人建议的方法
SET foreign_key_checks = 0;
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
。。。将禁用每个表的外键。这不适合在共享环境中使用
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
另一种方法是使用
ALTER TABLE `guestbook`
DROP FOREIGN KEY `guestbook_ibfk_1`
/
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
我们可以使用DML对数据进行排序,然后使用以下方法恢复外键:
ALTER TABLE `guestbook`
ADD CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`)
REFERENCES `guestbook` (`Id`)
/
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
但是有没有一种方法可以在不执行任何DDL的情况下更改数据?好的,我们可以插入一个新记录并更改当前记录以引用它:
INSERT INTO `guestbook` VALUES (212, 211)
/
UPDATE `guestbook`
SET `ThreadId` = 212
WHERE `Id` = 211
/
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
精明的观察者会注意到,我们最终还是会有一个相互依赖的关系,只是在记录之间。所以我们还没有真正的进步;我们现在有两个不能删除的记录,而不是一个。(顺便提一下,这适用于外键被删除或禁用时可能执行的任何DML)。因此,也许我们需要重新考虑数据模型。我们是用循环依赖关系还是层次结构来建模图
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
分层数据结构至少需要一个根节点,一个记录可以依赖于其他记录,但它本身不依赖于任何记录。实现这一点的通常方法是使外键列成为可选的。在层次结构的最顶层,该记录在该列中必须为NULL。是否应该只有一个这样的根节点,或者是否允许多个这样的根节点,这取决于您的业务规则
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
从建模的角度来看,这与作为自己的主记录的记录没有什么不同,但这是一个更直观的解决方案 无法删除自引用行是一个长期存在的问题
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
在许多遇到此问题的情况下,您可以在执行delete之前将外键设置为NULL,因此您的解决方法只会影响您想要的行(使用相同的where子句)。如果在delete上设置
,在我的外键上设置NULL,那么我就删除一个自引用。如果我没有在DELETE上指定,MySQL默认为RESTRICT
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
当然,请确保该列可为空
。您也可以尝试设置默认值,具体取决于默认值。但是请记住,NO ACTION
只是MySQL中RESTRICT
的别名
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
仅在MySQL 5.6上测试(最初发布此问题时未发布)。如果在外键上的DELETE CASCADE
操作中添加,则应该能够删除自引用的行
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/
CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ON DELETE CASCADE
与在DELETE SET NULL上使用相比,这有一个好处,那就是您不必更改模式以使“ThreadId”列为空。一行本身就是一个子行,这很奇怪。哇,这太棒了,您创建了一个表结构,一旦它有行,您就需要关闭约束以删除最后一行。很遗憾ID不是22。@Conrad:是的,但是任何自引用外键都可能发生这种情况$@K.J.正确,但通常“父ID”是可以为空的。谢谢,这似乎是目前最好的方法。应该在哪里运行此查询?在哪张桌子上,怎样?你最好编辑你的答案,让它更清楚。看起来是外键检查,而不是外键约束。:)我不明白为什么这个答案不是最好和正确的答案。在我们的用例中最简单的解决方案。谢谢:)
ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned
/