Удаление строки с помощью внешнего ключа

У меня есть таблица 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 был определен не null, также невозможно временно установить ThreadId на другое значение для удаления строки. Есть ли способ удалить строку без изменения определения таблицы или удаления всей таблицы?

Ответы

Ответ 1

Вы можете временно отключить ограничения внешнего ключа с помощью этого запроса:

SET foreign_key_checks = 0;

Ответ 2

Существует несколько обходных решений. Подход, предложенный другими...

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, который мы могли бы выполнить, когда внешний ключ отключен или отключен). Поэтому, возможно, нам нужно пересмотреть модель данных. Мы моделируем график с круговыми зависимостями или иерархией?

Для иерархической структуры данных требуется хотя бы один корень node, запись, на которую могут зависеть другие записи, но которая сама по себе не зависит от записи. Обычный способ реализации этого заключается в том, чтобы сделать столбец внешнего ключа необязательным. На верхнем уровне иерархии запись должна иметь NULL в этом столбце. Должен ли быть только один такой корень node, или разрешено ли нескольким, является вопросом для ваших бизнес-правил.

ALTER TABLE `guestbook` MODIFY `ThreadId`  int(10) unsigned
/

В терминах моделирования это не отличается от записи, которая является ее собственным мастером, но это более интуитивное решение.

Ответ 3

Невозможность удалить строку саморегуляции - это давний известный запрос ошибок/выдающихся функций в MySQL.

Во многих ситуациях, когда вы исправляете эту проблему, вы можете NULL для внешнего ключа перед выполнением удаления, поэтому ваше обходное решение затрагивает только те строки, которые вы намереваетесь (использует одно и то же предложение WHERE).

Ответ 4

Если вы помещаете действие ON DELETE CASCADE на свой внешний ключ, вы должны иметь возможность удалять строки, которые являются самостоятельными.

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

Выгода от использования ON DELETE SET NULL заключается в том, что вам не нужно изменять вашу схему, чтобы столбец "ThreadId" был обнулен.

Ответ 5

Ya временно отключить внешний ключ

set foreign_key_checks=0;

Ответ 6

Если вы установили ON DELETE SET NULL в свой внешний ключ, он позволяет мне удалять саморегуляцию. Если я не укажу ON DELETE, MySQL по умолчанию будет RESTRICT.

Конечно, убедитесь, что столбец NULLABLE. Вы также можете попробовать SET DEFAULT в зависимости от того, что такое по умолчанию. Но помните, что NO ACTION - это просто псевдоним RESTRICT в MySQL!

Проверено только на MySQL 5.6 (который не был выпущен, когда этот вопрос был первоначально отправлен).