Хорошее объяснение каскадного (ON DELETE / UPDATE) поведения

99

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

Например, если у меня есть две таблицы - Parentи Child- с внешним ключом для Childэтих ссылок Parentи имеет ON DELETE CASCADE, какие записи вызывают каскад и какие записи удаляются каскадом? Моим первым предположением будет Childудаление Parentзаписей при удалении записей, поскольку Childзаписи зависят от Parentзаписей, но ON DELETEэто неоднозначно; это может означать удаление Parentзаписи при Childудалении записи, или это может означать удаление Childзаписи при Parentудалении. Так что это?

Я хочу синтаксис был ON PARENT DELETE, CASCADE, ON FOREIGN DELETE, CASCADEили что - то подобное для устранения неясности. У кого-нибудь есть мнемоника для запоминания этого?

Johntron
источник

Ответы:

139

Если вам нравятся Parentи Childтермины , и вы чувствуете , что они легко помнить, вы можете , как перевод ON DELETE CASCADEнаLeave No Orphans!

Это означает, что при Parentудалении (уничтожении) строки в Childтаблице не должно остаться ни одной строки-сироты . Все дочерние элементы родительского ряда также будут удалены (удалены). Если у кого-либо из этих детей есть внуки (в другой таблице через другой внешний ключ), и они там ON DELETE CASCADEопределены, их тоже нужно убить (и всех потомков, если определен каскадный эффект).

Само FOREIGN KEYограничение также может быть описано как Allow No Orphans!(во-первых). Никогда не Childдолжно быть разрешено (записано) в дочерней таблице, если она не имеет Parent(строка в родительской таблице).

Для согласованности, ON DELETE RESTRICTможно перевести в (менее агрессивный) You Can't Kill Parents!Только бездетные строки могут быть уничтожены (удалены.)

ypercubeᵀᴹ
источник
3
Я чувствую, что чего-то не хватает в аналогии. У ребенка не может быть более одного родителя? В этом случае убийство одного из родителей сделает ребенка сиротой?
Jus12
7
@ Jus12 Нет, ограничения внешнего ключа работают только с 1 родителем. Это не хорошая аналогия в отношении этого аспекта.
ypercubeᵀᴹ
1
@ypercube: это не разрешено? Order(custID, itemID, orderID)где custIDссылается на первичный ключ в Customersтаблице и itemIDссылается на первичный ключ в Itemsтаблице. Не Orderбудет двух родителей?
Jus12
4
@ Jus12 Это, конечно, разрешено, но это будет 2 ограничения внешнего ключа. Тогда у каждого ребенка (заказа) будет родитель (клиент) и родитель (товар). Поведение 2 ФК может отличаться. (Так, например, может случиться так, что убийство клиентов убьет всех их (заказов) детей, но убийство предметов не убьет их заказов.)
ypercubeᵀᴹ
1
Родительская аналогия все еще может работать, если мы не скажем «сирота». Если в записи о ребенке есть две ссылки на двух отдельных родителей, это все равно можно считать ребенком разведенной пары. Ограничить: «Я не позволю тебе убить мою маму» Каскад: «Если ты убьешь моего отца, я тоже умру»
Кристофер МакГоуэн
31

Например, если у меня есть две таблицы - «Родительская» и «Дочерняя» - где записи «Дочерние» принадлежат родительским записям, для какой таблицы требуется «ON DELETE CASCADE»?

ON DELETE CASCADE - это необязательное условие в объявлении внешнего ключа. Так же и с декларацией внешнего ключа. (Имеется в виду в таблице «ребенок».)

... это может означать удаление родительской записи при удалении дочерней записи, или это может означать удаление дочерней записи при удалении родительской записи. Так что это?

Один из способов интерпретации объявления внешнего ключа: «Все допустимые значения для этого столбца взяты из« that_column »в« that_table »». Когда вы удаляете строку в «дочерней» таблице, это никого не волнует. Это не влияет на целостность данных.

Когда вы удаляете строку из «родительской» таблицы - из «that_table» - вы удаляете допустимое значение из возможных значений для «дочерней» таблицы. Чтобы сохранить целостность данных, вы должны что- то сделать с «дочерней» таблицей. Каскадное удаление - это одна вещь, которую вы можете сделать.

Майк Шеррилл 'Cat Recall'
источник
2

SQL: 2011 Spec

Есть пять вариантов ON DELETE, и ON UPDATEэто может применяться к FOREIGN KEY. Они вызываются <referential actions>непосредственно из спецификации SQL: 2011

  • ON DELETE CASCADE: если строка ссылочной таблицы удаляется, то все соответствующие строки в ссылочной таблице удаляются.
  • ON DELETE SET NULL: если строка ссылочной таблицы удаляется, то все ссылочные столбцы во всех соответствующих строках ссылочной таблицы должны быть установлены на ноль.
  • ON DELETE SET DEFAULT: если строка ссылочной таблицы удаляется, то для всех ссылочных столбцов во всех соответствующих строках ссылочной таблицы должно быть установлено значение столбца по умолчанию.
  • ON DELETE RESTRICT: запрещено удалять строку из ссылочной таблицы, если в этой строке есть какие-либо совпадающие строки в ссылочной таблице.
  • ON DELETE NO ACTION (по умолчанию) : нет ссылочного действия удаления; ссылочное ограничение определяет только проверку ограничения.

Внешний ключ устанавливает зависимые отношения. <referential action>Определяет , что происходит , когда растворяются отношения.

Пример / Метафора / Объяснение

В этом примере мы примем общую модель общества и экономики: где каждый business- это компания, которая поддерживает отношения с bourgeoisieпомощью a fatcat_owner.

CREATE TABLE bourgeoisie(
  fatcat_owner varchar(100) PRIMARY KEY
);
INSERT INTO bourgeoisie(fatcat_owner) VALUES
  ( 'Koch Brothers' );

CREATE TABLE business (
  name         varchar(100),
  fatcat_owner varchar(100) REFERENCES bourgeoisie
);
INSERT INTO business(name, fatcat_owner)
  VALUES ('Georgia-Pacific', 'Koch Brothers');

Если все businessони непосредственно затронуты их bourgeoisieпутем, fatcat_ownerто что вы делаете после рабочей революции, когда вы очищаете ее fatcat_ownerи имеете бесклассовое общество?

-- Viva la revolución 
BEGIN;
  DELETE FROM bourgeoisie;
END;

У вас есть несколько вариантов здесь,

  • Останови революцию. На языке SQL RESTRICT. Некоторые люди считают, что это меньшее зло, но обычно они ошибаются.
  • Позвольте этому продолжаться. Если так, когда произойдет революция, SQL дает вам четыре варианта,

    • SET NULL- оставь это поле пустым. Кто знает, может быть, капитализм восстановится bourgeoisie, и олигархи заполнят список fatcat_owners. Важное примечание, столбец должен быть NULLABLE(нет NOT NULL), иначе этого не может быть.
    • SET DEFAULT- Может быть, вы DEFAULTсправились с этим? А DEFAULTможет вызвать функцию. Возможно, ваша схема уже готова к революции.
    • CASCADE- нет контроля ущерба. Если bourgeoisieидет, то и business. Если бизнес должен иметь fatcat_pig, то иногда имеет смысл потерять данные, а не иметь некоммерческий бизнес в businessтаблице.
    • NO ACTION- по сути, это метод задержки проверки, в MySQL он ничем не отличается от RESTRICT, но в PostgreSQL вы сможете сделать это

      -- Not a real revolution.
      -- requires constraint be DEFERRABLE INITIALLY DEFERRED
      BEGIN;
        SET CONSTRAINTS ALL DEFERRED;
        DELETE FROM bourgeoisie;
        INSERT INTO bourgeoisie VALUES ( 'Putin' );
        UPDATE business SET fatcat_pig = 'Putin';
      END;
      

      В такой системе ограничение проверяется только перед фиксацией транзакции. Это может привести к остановке революции, но вы можете восстановить транзакцию - для некоторой степени «восстановления».

Эван Кэрролл
источник
Есть ли referencedтаблица означает родительскую таблицу и referencingтаблица означает ребенок таблицу?
sg552
@ sg552 Да, вы правильно поняли.
informatik01
1

Простая мнемоника будет

УДАЛИТЬ родительский CASCADE [удаляя] здесь

Это говорит вам, какие операции удаления (удаления родительского элемента) каскадируются, куда идет оператор ON DELETE CASCADE (для дочернего элемента ) и что удаляется (дочерний элемент).

msouth
источник
-3

ну, возможно, мы можем рационализировать синтаксис. Давайте возьмем пример Python:

class Parent(self):
    # define parent's fields

class Child(self):    
    # define child's fields
    parent_pk_is_childs_foreign_key = models.ForeignKey(Parent, on_delete=models.CASCADE)

то, что в этой строке написано on_delete для Parent (что случайно упоминается в выражении), пожалуйста, каскадно удалите дочерний объект. Вот почему оператор CASCADE определяется на дочернем уровне, он отмечает тех дочерних элементов, которые необходимо удалить.

Например, если у вас был другой класс

class GrownUpChild(self):    
        # define grown up child's fields
        parent_pk_is_childs_foreign_key = models.ForeignKey(Parent, on_delete=models.DO_NOTHING)

эта структура четко показала бы, кого из детей нужно удалить (Child), а каких оставить (GrownUpChild), хотя и осиротеть

[Редактировать: учитывая контекст обсуждения, особенно в случаях on_delete = models.CASCADE и т. Д.], На самом деле часто бывает желательным оставлять детей удаленного родителя из-за причин аудита и отчетности, а также из-за случайного восстановления удаления. [конечно, программное обеспечение уровня предприятия будет построено на основе такого поведения и будет помечать удаленные записи как удаленные = 1 вместо фактического их удаления, а также не будет включать их в какие-либо запросы для внешнего интерфейса, за исключением некоторых специально разработанных отчетов. Кроме того, он будет иметь функцию очистки удаленных == 1 записей из базы данных, которая обычно выполняется администратором пользовательского интерфейса, часто избегая какого-либо участия со стороны администратора базы данных.]

Георгий Могилевский
источник
1
«На самом деле часто бывает желательным оставлять детей удаленного родителя из-за причин аудита и отчетности, а также из-за случайного удаления», - это было бы катастрофично для (нормальной) базы данных.
Дезсо
@dezso спасибо за ваш вклад. Тем не менее, CRM-системы на уровне предприятия делают именно это.
Георгий Могилевский
TBH, который не делает это более разумным. Однажды я получил задание починить дерьмо в результате такого подхода - никакой радости, кроме зарплаты.
Дезсо
Вы говорите как опытный администратор базы данных :) Я полностью слышу вашу точку зрения. Программное обеспечение, которое я упомянул выше, которое делает это, на самом деле имеет функцию удаления удаленных = 1 вручную, поэтому этот вызов должен сделать администратор приложения. Обычно администратор базы данных даже не участвует в поддержании этого аспекта. И, кроме того, весь класс базы данных программного обеспечения построен на основе этой концепции, поэтому он всегда проверяет наличие удаленного флага в грубых операциях
Георгий Могилевский
Да, это известный и вменяемый шаблон, но тогда вам, возможно, следует изменить формулировку выше, чтобы отразить это.
Дезсо