使用自引用外键删除行

K J*_*K J 16 mysql sql 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临时设置为不同的值以删除该行.有没有办法删除行而不更改表的定义或删除整个表?

Bur*_*zel 22

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

SET foreign_key_checks = 0;
Run Code Online (Sandbox Code Playgroud)


haw*_*kip 6

无法删除自引用行是MySQL 中长期存在的已知错误/未解决的功能请求

在许多遇到此问题的情况下,您可以在执行删除之前将外键设为 NULL,因此您的解决方法仅影响您想要的行(使用相同的 WHERE 子句)。


APC*_*APC 5

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

SET foreign_key_checks = 0;
Run Code Online (Sandbox Code Playgroud)

...将禁用每个表的外键.这不适合在共享环境中使用.

另一种方法是使用删除外键

ALTER TABLE `guestbook` 
    DROP FOREIGN KEY `guestbook_ibfk_1`
/
Run Code Online (Sandbox Code Playgroud)

我们可以使用DML对数据进行排序,然后使用以下命令恢复外键:

ALTER TABLE `guestbook` 
    ADD CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) 
        REFERENCES `guestbook` (`Id`)
/
Run Code Online (Sandbox Code Playgroud)

但有没有办法在不执行任何DDL的情况下更改数据?好吧,我们可以插入一条新记录并更改当前记录以引用它:

INSERT INTO `guestbook`  VALUES (212, 211)
/
UPDATE `guestbook` 
SET `ThreadId` = 212
WHERE `Id` = 211
/
Run Code Online (Sandbox Code Playgroud)

精明的观察者会注意到我们仍然只有记录之间的共同依赖.所以我们还没有真正进步; 我们现在有两个我们无法删除的记录,而不是一个.(顺便说一下,这适用于在删除或禁用外键时我们可能执行的任何DML).所以,也许我们需要重新考虑数据模型.我们是否使用循环依赖关系或层次结构建模图形?

分层数据结构至少需要一个根节点,其他记录可以依赖的记录,但它本身不依赖于记录.实现此目的的通常方法是使外键列可选.在层次结构的最顶层,记录在该列中必须为NULL.是否应该只有一个这样的根节点或是否允许多个根节点是您的业务规则的问题.

ALTER TABLE `guestbook` MODIFY `ThreadId`  int(10) unsigned
/
Run Code Online (Sandbox Code Playgroud)

在建模术语中,这与作为其自己的主记录的记录没有区别,但它是更直观的解决方案.


The*_*nce 5

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

CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ON DELETE CASCADE
Run Code Online (Sandbox Code Playgroud)

使用它的好处ON DELETE SET NULL是您不必更改架构以使“ThreadId”列可以为空。