В каком порядке обрабатываются ограничения ON DELETE CASCADE?

StackOverflow https://stackoverflow.com/questions/60168

Вопрос

Вот пример того, что у меня происходит:

CREATE TABLE Parent (id BIGINT NOT NULL,
  PRIMARY KEY (id)) ENGINE=InnoDB;

CREATE TABLE Child (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  CONSTRAINT fk_parent FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE) ENGINE=InnoDB;

CREATE TABLE Uncle (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  childid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  KEY (childid),
  CONSTRAINT fk_parent_u FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE,
  CONSTRAINT fk_child FOREIGN KEY (childid) REFERENCES Child (id)) ENGINE=InnoDB;

Обратите внимание, что для отношений «дядя-потомок» нет ON DELETE CASCADE;то естьудаление дочернего элемента не удаляет его дядю(ей) и наоборот.

Когда у меня есть Родитель и Дядя с одним и тем же Дочерним элементом, и я удаляю Родителя, это кажется например, InnoDB должен иметь возможность просто «разобраться» и позволить каскаду распространяться по всему семейству (т. е.удаление родителя также удаляет дядю и ребенка).Однако вместо этого я получаю следующее:

  ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`cascade_test/uncle`, CONSTRAINT `fk_child` FOREIGN KEY (`childid`) REFERENCES `child` (`id`))

InnoDB пытается каскадно удалить дочерний элемент перед дядей, которые ссылаются на него.

Я что-то пропустил?Это предполагаемый потерпеть неудачу по какой-то причине, которую я не понимаю?Или есть какой-то трюк, чтобы заставить его работать (или это ошибка в MySQL)?

Это было полезно?

Решение

Как вы сказали, родительское удаление запускает дочернее удаление, и я не знаю, почему оно переходит в дочернюю таблицу перед таблицей дяди.Я предполагаю, что вам придется взглянуть на код базы данных, чтобы знать наверняка, но я уверен, что существует алгоритм, который выбирает, к каким таблицам следует каскадировать в первую очередь.

Система на самом деле не «вычисляет» вещи так, как вы здесь подразумеваете, а просто следует своим правилам ограничений.Проблема в том, что созданная вами схема сталкивается с ограничением, которое не позволяет ей пройти дальше.

Я понимаю, что ты говоришь..если он сначала попадет в таблицу дяди, он удалит запись, а затем удалит дочерний элемент (и не попадет в каскад дяди из удаления дочернего элемента).Но даже в этом случае я не думаю, что схема будет построена так, чтобы полагаться на такое поведение в реальности.Я думаю, что единственный способ узнать наверняка, что происходит, - это просмотреть код или попросить одного из программистов mysql/postgresql рассказать, как он обрабатывает ограничения fk.

Другие советы

В более простом случае, что произойдет, если запись будет удалена из Child и на нее будет ссылаться Uncle?Это не указано, поэтому ограничения в любом случае не работают.

Если удаление дочернего элемента не удаляет его дядей, что тогда происходит?Uncle.childid не может иметь значение null.

Вам нужна одна из этих трех вещей:

  1. Uncle.childid может иметь значение NULL, и вы хотите, чтобы ON DELETE SET NULL для childid.
  2. Uncle.childid не может иметь значение NULL, и для childid требуется ON DELETE CASCADE.
  3. Childid не принадлежит Uncle, и вам нужно отношение ChildsUncle с ограничениями внешнего ключа ON DELETE CASCADE как для Child, так и для Uncle.Uncleid будет потенциальным ключом для этого отношения (т.е.оно должно быть уникальным).

@Matt Solnit, прежде всего, это действительно хороший вопрос, и, насколько я знаю, когда запись из Parent должна быть удалена, тогда innodb сначала пытается определить, какие другие таблицы содержат ссылки на нее, чтобы он мог удалить из них запись как хорошо.В вашем случае это дочерняя таблица и таблица Uncle, теперь кажется, что в этом случае он решает сначала удалить запись из дочерней таблицы и, таким образом, повторяет тот же процесс для дочерней таблицы и в конечном итоге терпит неудачу, поскольку дядя хранит ссылку на дочернюю таблицу, но не "ON". DELETE CASCADE» и «ON DELETE SET NULL» не указаны для fk_child FK в таблице Uncle.Однако похоже, что если innodb сначала пытается удалить запись из таблицы Uncle, после чего удаление должно пройти гладко.Хорошо, поразмыслив, я думаю, что, поскольку innodb следует модели ACID, он выбирает Child вместо Uncle, чтобы начать процесс удаления, потому что, если он начинается с Uncle, даже тогда удаление в Child все равно может завершиться неудачей, например.предположим, что таблица Friend имеет ключ fk_child (аналог Uncle) без ON DELETE CASCADE, теперь это все равно привело бы к сбою всей транзакции, и поэтому мне кажется, что это правильное поведение.Другими словами, innodb начинается с таблицы, которая может привести к возможному сбою транзакции, но это моя теория, на самом деле это может быть совершенно другая история.:)

дизайн вообще неправильный.У вас должна быть одна таблица с отношениями родитель-потомок (буквально).Тогда вычислить дядей (и теть) можно запросом

select id from persons where -find all children of the grandparents
parent id in (
select parentid from persons --find the grandparents
where id in (
select parentid from persons --find the parents
where id=THECHILD) )
minus --and take out the child's parents
select parentid from persons
where id=THECHILD

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top