使用自引用外键删除行

时间:2011-01-01 08:15:20

标签: 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` (`Id`)
) ENGINE=InnoDB;

目前表中只有一行:

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列的定义不为null,因此也不可能将ThreadId临时设置为不同的值以删除该行。有没有办法删除行而不更改表的定义或删除整个表?

6 个答案:

答案 0 :(得分:22)

您可以使用此查询临时禁用外键约束:

SET foreign_key_checks = 0;

答案 1 :(得分:5)

有几种解决方法。其他人提出的方法......

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
/

在建模术语中,这与作为其自己的主记录的记录没有什么不同,但它是一种更直观的解决方案。

答案 2 :(得分:4)

如果对外键执行ON DELETE CASCADE操作,则应该能够删除自引用的行。

CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ON DELETE CASCADE

这比使用ON DELETE SET NULL带来的好处是,您不必更改架构以制作" ThreadId"列可以为空。

答案 3 :(得分:3)

无法删除自引用行是一个长期的known bug/outstanding feature request in MySQL

在许多情况下,你遇到了这个问题,你可以在执行删除之前使外键为NULL,这样你的变通方法只影响你想要的行(使用相同的WHERE子句)。

答案 4 :(得分:0)

Ya暂时禁用外键

set foreign_key_checks=0;

答案 5 :(得分:0)

如果你在我的外键上设置ON DELETE SET NULL,我就删除自我引用。如果我没有指定ON DELETE,则MySQL默认为RESTRICT

当然,请确保该列为NULLABLE。您也可以尝试SET DEFAULT,具体取决于默认值。但请记住NO ACTION只是MySQL中RESTRICT的别名!

仅在MySQL 5.6上测试过(最初发布此问题时尚未发布)。