В каком порядке обрабатываются ограничения ON DELETE CASCADE? - PullRequest
9 голосов
/ 13 сентября 2008

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

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)?

Ответы [ 4 ]

8 голосов
/ 13 сентября 2008

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

Если удаление ребенка не удаляет его дядю, то что происходит вместо этого? Uncle.childid не может быть нулевым.

То, что вы хотите, это одна из этих трех вещей:

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

Удаление родительского элемента вызывает удаление дочернего элемента, как вы указали, и я не знаю, почему он попадает в дочернюю таблицу перед таблицей дядюшки. Я предполагаю, что вам нужно было бы взглянуть на код dbms, чтобы точно знать, но я уверен, что есть алгоритм, который выбирает, какие таблицы каскадировать в первую очередь.

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

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

0 голосов
/ 07 мая 2013

@ Мэтт Солнит: во-первых, это действительно хороший вопрос, и, насколько я знаю, когда запись из Parent должна быть удалена, тогда innodb сначала пытается определить, какие другие таблицы содержат ссылки на нее, чтобы она могла удалить запись. от них тоже. В вашем случае это дочерняя таблица и таблица дяди, теперь кажется, что в этом случае она решает сначала удалить запись из дочерней таблицы и, таким образом, повторяет тот же процесс для дочернего элемента и в конечном итоге завершается ошибкой, поскольку дядя хранит ссылку на дочернюю таблицу, но не имеет значения «ON «УДАЛИТЬ КАСКАД», а также «УДАЛИТЬ УСТАНОВИТЬ НУЛЬ» задано для fk_child FK в таблице Uncle. Тем не менее, похоже, что , если innodb сначала пытается удалить запись из таблицы Uncle, тогда удаление должно было произойти гладко. Хорошо подумав, я думаю, что, поскольку innodb следует модели ACID, таким образом, он выбирает Child вместо Uncle, чтобы начать процесс удаления, потому что, если он начинается с Uncle, даже тогда удаление в Child могло все еще быть неудачным, например. предположим, что у таблицы Friend есть ключ fk_child (аналогично Uncle) без ON DELETE CASCADE, теперь это все равно привело бы к сбою всей транзакции, и поэтому мне это кажется правильным. Другими словами, innodb начинается с таблицы, которая может вызвать возможный сбой транзакции, но это моя теория, в действительности это может быть совсем другая история. :)

0 голосов
/ 13 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...