Гарне пояснення поведінки каскаду (НА ВИДАЛЕННЯ / ОНОВЛЕННЯ)


98

Я не створюю схеми щодня, але коли я це роблю, я намагаюся правильно налаштувати каскадні оновлення / видалення, щоб полегшити адміністрування. Я розумію, як працюють каскади, але ніколи не можу згадати, який столик є.

Наприклад, якщо у мене є дві таблиці - 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або що - щось подібне для усунення неясності. Хтось має мнемоніку, щоб запам'ятати це?

Відповіді:


138

Якщо вам подобаються 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!Загинути можуть лише бездітні рядки (видалено.)


3
Я відчуваю, що чогось ще не вистачає в аналогії. Не може дитина мати більше одного батька? У цьому випадку чи вбивство одного з батьків зробить дитину сиротою?
Jus12

7
@ Jus12 Ні, обмеження зовнішніх ключів працюють лише з одним батьком. Це не дуже хороша аналогія щодо цього аспекту.
ypercubeᵀᴹ

1
@ypercube: Це заборонено? Order(custID, itemID, orderID)де custIDпосилається на первинний ключ у Customersтаблиці та itemIDпосилається на первинний ключ у Itemsтаблиці. Не Orderбуде двох батьків?
Jus12

4
@ Jus12 Це, звичайно, дозволено, але це було б 2 зовнішніх ключових обмеження. Тоді кожна дитина (замовлення) матиме батька (замовника) та батьків (предмет). Однак поведінка 2-х ФК може відрізнятися. (Так, наприклад, може статися так, що замовники вбивають усіх своїх дітей (замовлення), але вбивство предметів не вбиває їх замовлення.)
ypercubeᵀᴹ

1
Батьківська аналогія все ще може працювати, якщо ми не скажемо «сирота». Якщо у в'їзді на дитину є дві згадки про двох окремих батьків, це все ще може розглядатися як дитина розлученої пари. Обмежити: "Я не дам тобі вбити мою маму" Каскад: "Якщо ти вб'єш мого тата, я теж помру"
Крістофер Макгован,

31

Наприклад, якщо у мене є дві таблиці - батьківська та дочірня - де дочірні записи належать батьківським записам, яка таблиця потребує ВІДКРИТИ КАСКАД?

ON DELETE CASCADE - необов'язковий пункт у декларації з іноземним ключем. Так це йде з декларацією зовнішнього ключа. (Значення, у таблиці "дитина".)

... це може означати видалення батьківського запису при видаленні дочірнього запису, або це може означати видалення дочірнього запису при видаленні батьків. Так що це?

Один із способів інтерпретації декларації з іноземним ключем - "Усі дійсні значення для цього стовпця походять з" that_column "у" that_table "." Коли ви видаляєте рядок у таблиці "дочірня", ніхто не цікавиться. Це не впливає на цілісність даних.

Видаляючи рядок із таблиці "батьків" - з "that_table" - ви видаляєте дійсне значення з можливих значень для "дочірньої" таблиці. Щоб зберегти цілісність даних, вам потрібно щось зробити до «дочірньої» таблиці. Каскадні вилучення - це одне, що ви можете зробити.


2

SQL: Spec. 2011 р

Існує п’ять варіантів 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через 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справу з цим? A 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

0

Простий мнемічний був би

ON DELETE з батьківського CASCADE [видаленням] тут

Це говорить вам про те, які видалення (вилучення батьків) отримують каскад , куди йде оператор ON DELETE CASCADE (про дитину) та що видаляється (дитина).


-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 з Батька (що випадково згадується у заяві), будь ласка, каскадуйте видалення на дитину. Ось чому оператор CASCADE визначений на рівні дитини, він позначає тих дітей, яких потрібно видалити

Наприклад, якщо у вас був інший клас

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

ця структура чітко показує, кого з дітей потрібно видалити (Дитину), а які - залишитися (GrownUpChild), хоч і осиротіли.

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


1
"Насправді часто бажана поведінка залишати дітей видаленого батька через причини аудиту та звітності, а також відновлення випадкових видалень" - це було б згубно для (нормальної) бази даних.
dezso

@dezso дякую за ваш внесок. Однак багато CRM-систем на рівні підприємства роблять саме це.
Георгій Могилевський

TBH, що не робить його більш розумним. Я одного разу отримав доручення виправляти лайно внаслідок такого підходу - ніякої радості, крім зарплати.
dezso

ти схожий на кмітливий адміністратор бази даних :) Я цілком чую твою думку. Програмне забезпечення, яке я згадав вище, це робить, насправді має функцію видалення видалених = 1 вручну, тому адміністратор програми повинен здійснити цей виклик. Зазвичай адміністратор бази даних навіть не бере участь у підтримці цього аспекту. І крім того, весь клас баз даних програмного забезпечення побудований навколо концепції, тому він завжди перевіряє на видалений прапор у грубих операціях
Георгій Могилевський

Так, це відомий і здоровий зразок - але тоді, можливо, слід змінити формулювання вище, щоб відобразити це.
dezso
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.