Коли / навіщо використовувати Cascading у SQL Server?


149

Під час налаштування сторонніх ключів у SQL Server, за яких обставин ви повинні мати його каскад при видаленні чи оновленні та які міркування за ним?

Це, мабуть, стосується і інших баз даних.

Я найбільше шукаю конкретні приклади кожного сценарію, бажано від того, хто їх успішно використовував.


4
Це питання не здається суворо пов'язаним із SQL Server і більше схоже на теоретичне, загальне питання. Буде кориснішим для спільноти, якщо ви видалите sql-serverтег.
клапас

4
@clapas Чесно кажучи, якби я сьогодні запитав це, це було б поза темою. Якщо б не високі погляди / голоси, що свідчать про те, що він має значення для спільноти, я би просто видалив його.
Joel Coehoorn

6
@JoelCoehoorn - Очевидно, що ці питання мають значення. Це значення не розсіюється через час. Питання в моєму розумінні - яке значення ми втрачаємо, забороняючи такі питання сьогодні?
P.Brian.Mackey

2
@ P.Brian.Mackey Тут, Тут! Деякі найкращі запитання / відповіді, які я бачив, - це ті, за яких голосування або відмовились за те, що вони поза темою ... але ви можете сказати ВЕЛИЧЕСНУ кількість голосів, що у багатьох було саме таке питання!
Ентоні Гріггс

Каскадні дії роблять серійні замки.
Мітч Пшеничний

Відповіді:


126

Підсумок того, що я бачив досі:

  • Деяким людям зовсім не подобається каскадування.

Каскад Видалити

  • Каскадне видалення може мати сенс, коли семантика відносин може включати ексклюзивний опис "є частиною ". Наприклад, запис OrderLine є частиною його батьківського замовлення, і OrderLines ніколи не поділяться між декількома замовленнями. Якби Орден був зниклий, OrderLine також повинна бути проблемою, а лінія без Ордена була б проблемою.
  • Канонічним прикладом для видалення каскаду є SomeObject та SomeObjectItems, де немає жодного сенсу для записів елементів без відповідного основного запису.
  • Ви повинні НЕ використовувати Cascade Delete , якщо ви з збереженням історії або з допомогою «м'якого / логічного видалення» , де ви тільки встановити віддалений бітний стовпець 1 / вірно.

Каскадне оновлення

  • Каскадне оновлення може мати сенс, коли ви використовуєте реальний ключ, а не сурогатний ключ (стовпчик ідентифікації / автопосилення) у таблицях.
  • Канонічний приклад для Cascade Update - це коли ви маєте змінний зовнішній ключ, як-от ім’я користувача, яке можна змінити.
  • Ви повинні НЕ використовувати Cascade Update з ключами, які Ідентичність / Autoincrement стовпці.
  • Cascade Update найкраще використовувати в поєднанні з унікальним обмеженням.

Коли використовувати каскад

  • Ви можете отримати додаткове підтвердження від користувача, перш ніж дозволити операцію каскадувати, але це залежить від вашої програми.
  • Каскад може привести вас до неприємностей, якщо неправильно налаштувати зовнішні ключі. Але ви повинні гаразд, якщо зробите це правильно.
  • Не розумно користуватися каскадом, перш ніж все це зрозуміти. Однак це корисна особливість, і тому варто витратити час, щоб зрозуміти.

3
Зауважте, що каскадні оновлення також часто використовуються там, коли "так звані" природні клавіші не є справжніми ефективними унікальними ключами. Насправді я переконаний, що оновлення каскаду потрібні лише з погано нормалізованими моделями баз даних, і вони є відкритими воротами для брудних таблиць і безладного коду.
Філіп Гронд'є

1
Вам не вистачає важливого моменту, якщо каскад може створити величезні проблеми щодо продуктивності, якщо багато записів дітей.
HLGEM

13
@HLGEM - я не бачу відповідності. Якщо каскадні операції спричиняють уповільнення, еквівалентний ручний процес або спричинить таке ж уповільнення, або не буде належним чином захищений у випадку, якщо транзакцію потрібно повернути назад.
Джоель Куехорн

3
Чому було б важливо, чи є оновлення каскаду в стовпці «Ідентифікація» або стовпці з автоматичним збільшенням? Я можу зрозуміти , чому це не було б необхідно , тому що ви не повинні змінювати ці (довільні) значення, але якщо один з них зробив зміни, по крайней мере , довідкова цілісність буде недоторканим.
Кенні Евітт

3
10 куль? Тепер ми знаємо, що Джоел не стріляє з револьвера.
Ніл N

68

Іноземні ключі - найкращий спосіб забезпечити референтну цілісність бази даних. Уникати каскадів через магію - це як писати все на зборах, тому що ви не довіряєте магії компіляторів.

Погано - неправильне використання сторонніх ключів, наприклад, створення їх назад, наприклад.

Приклад Хуана Мануеля - це канонічний приклад, якщо ви використовуєте код, є набагато більше шансів залишити помилкові DocumentItems в базі даних, які прийдуть і вас кусають.

Каскадні оновлення корисні, наприклад, коли у вас є посилання на дані чимось, що може змінитися, скажімо, первинним ключем таблиці користувачів є ім'я, комбінація прізвища. Тоді ви хочете, щоб зміни в цій комбінації поширилися там, де вони посилаються.

@Aidan, Ця чіткість, на яку ви посилаєтесь, виходить з високою вартістю, шанс залишити помилкові дані у вашій базі даних, що не мало . Для мене, як правило, це просто недостатнє ознайомлення з БД і неможливість знайти, які ФК є, перш ніж працювати з БД, що сприяє цьому страху. Або це, або постійне зловживання каскадом, використання його там, де сутності не були концептуально пов’язані, або де потрібно зберігати історію.


7
Використання такого роду "природного" первинного ключа - це вкрай погана ідея.
Нік Джонсон

4
RE: Коментар, адресований Айдану. Ні, відмова від CASCADE у ФК не збільшує шансів залишити помилкові дані. Це зменшує ймовірність, що команда вплине на більше даних, ніж очікувалося, і збільшить код. Вихід із ФК повністю залишає шанс помилкових даних.
Шеннон Северанс

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

1
Я фактично кодував системи, де було прийнято управлінське рішення, що користувачам доведеться явно видалити всіх дітей в основній деталі, перш ніж вони зможуть видалити майстер просто, щоб змусити користувача задуматися. Не використовувати каскади, коли логічно можна було, є подібним видом пожеж.
Круачан

6
@Cruachan: На мій погляд, правило є простим. Якщо дані не настільки сильно пов’язані, як безкорисні без батьківських даних, вони не гарантують каскадних відносин. Це я, на що я спробував звернутися в останній фразі своєї відповіді.
Винко Врсалович

17

Я ніколи не використовую каскадні делети.

Якщо я хочу щось видалити з бази даних, я хочу чітко сказати базі даних, що я хочу вийняти.

Звичайно, вони є функцією, доступною в базі даних, і можуть бути випадки, коли їх нормально використовувати, наприклад, якщо у вас є таблиця "замовлення" та таблиця "orderItem", ви можете очистити елементи при видаленні замовлення.

Мені подобається чіткість, яку я отримую від того, щоб це робити в коді (або збереженій процедурі), а не в магії.

З цієї ж причини я і не шанувальник тригерів.

Щось слід помітити, що якщо ви видалите "замовлення", ви отримаєте звіт "1 рядок, який впливає", навіть якщо каскадне видалення видалило 50 "порядку замовлення".


34
Чому б не позбутися і первинних ключів? Ви отримаєте чіткість забезпечення унікальних значень у своєму коді.
MusiGenesis

4
@MusiGenesis, Айдан не виступав за вилучення ФК. FK все ще захищає дані, але без CASCADE ON .... несподівана магія не відбувається.
Шеннон Северанс

7
@Vinko: Видалення та оновлення мають чітко визначену семантику за замовчуванням. Зміна поведінки за допомогою каскаду чи тригера для виконання більшої роботи залишає шансів більше, ніж було заплановано. Ні, я не працюю без тестування, і так, мої бази даних задокументовані. Але чи пам’ятаю я кожну частину документації під час написання коду? Якщо я хочу семантику вищого рівня, як-от видалити батьків та дітей, ніж я напишу і використовую SP для цього.
Шеннон Северанс

3
@Vinko. проблема магії полягає не в компетентних розробниках або в DBA, це через Джо Інтерена через 5 років, якому було надано "просте" завдання з обслуговування, коли DBA знаходиться у відпустці, і хто потім викручує корпоративні дані, не розуміючи цього. Каскади мають своє місце, але важливо врахувати всі обставини, включаючи людські фактори, перш ніж їх розгорнути.
Круачан

5
@Vinko: Чому "Gasp" SP? SP - це зухвало шлях до того, де база даних є важливим корпоративним активом. Існує вагомий аргумент в таких обставинах, які говорили про обмеження доступу всіх даних до SP, або принаймні всіх, крім Select. Дивіться мою відповідь під stackoverflow.com/questions/1171769/…
Cruachan

13

Я багато працюю з каскадними делетами.

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

Однак, у мене є одна проблема з каскадними видаленнями і циркулярними посиланнями. Це часто призводить до частин бази даних, які не мають каскадних видалень.


1
Я знаю, що це дуже давно, але +1 для згадування циркулярної проблеми з CASCADE DELETE.
Code Maverick

2
Пробачте питання нооба: що насправді трапиться, якщо ви отримаєте круговий довідник?
Тім Ловелл-Сміт

10

Я багато працюю над базою даних і рідко знаходжу каскадні видалення корисними. Один раз, коли я їх ефективно використав, - це база даних звітів, яка оновлюється нічним завданням. Я переконуюсь, що будь-які змінені дані імпортуються правильно, видаливши будь-які записи верхнього рівня, які були змінені з моменту останнього імпорту, а потім повторно імпортувати змінені записи та все, що стосується їх. Це врятує мене від необхідності писати багато складних видалень, які виглядають знизу вгору моєї бази даних.

Я не вважаю видалення каскаду таким же поганим, як тригери, оскільки вони видаляють лише дані, тригери всередині можуть мати всілякі неприємні речі.

Як правило, я взагалі уникаю справжнього видалення та використовую логічні делети (тобто, маючи стовпчик бітів під назвою isDeleted, який встановлюється на true).


2
Ти мені цікаво дізнатися більше. Чому ви сильно віддаєте перевагу логічним видаленням? Чи пов’язані дані, з якими ви працюєте?
Тім Ловелл-Сміт

9

Один із прикладів - коли у вас є залежності між сутностями ... тобто: Document -> DocumentItems (коли ви видаляєте Document, DocumentItems не має причин існувати)


5

Використовуйте каскадне видалення там, де ви хочете видалити запис з FK, якщо його видалений запис PK був видалений. Іншими словами, де запис безглуздий без референційного запису.

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


5

ON Видалити каскад:

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

Якщо при видаленні каскаду не використовується, то для відносної цілісності буде підвищена помилка .

Каскад оновлень оновлень:

Коли ви хочете, щоб зміни в первинному ключі були оновлені в зовнішньому ключі


5

Я чув про DBA та / або "Політику компанії", які забороняють використовувати "On Cascade Delete" (та інші) виключно через поганий досвід у минулому. В одному випадку хлопець написав три тригери, які в результаті передзвонили один одному. Три дні відновлення призвели до повної заборони тригерів, все через дії одного іджіта.

Звичайно, іноді потрібні тригери замість "Каскаду на видалення", наприклад, коли потрібно зберегти деякі дочірні дані. Але в інших випадках цілком справедливо використовувати метод каскаду On Delete. Ключовою перевагою "On Delete Cascade" є те, що він захоплює ВСІХ дітей; користувацька письмова процедура тригера / зберігання може не бути, якщо вона не закодована правильно.

Я вважаю, що розробнику слід дозволити приймати рішення, виходячи з того, що таке розвиток і що говорить специфікація. Заборона килимів, заснована на поганому досвіді, не повинна бути критеріями; думковий процес «Ніколи не використовувати» в кращому випадку є драконічним. Виклик судового рішення повинен здійснюватися щоразу, і змінюватись у міру зміни бізнес-моделі.

Чи не в цьому полягає розвиток?


Я не думаю , що було б видалити всі ... ви маєте в виду особливість на насправді робить те , що він говорить , що це робить? ...
Джоел Куехорн

3

Однією з причин ввести каскадне видалення (а не робити це в коді) є підвищення продуктивності.

Випадок 1: З видаленням каскаду

 DELETE FROM table WHERE SomeDate < 7 years ago;

Випадок 2: Без каскадного видалення

 FOR EACH R IN (SELECT FROM table WHERE SomeDate < 7 years ago) LOOP
   DELETE FROM ChildTable WHERE tableId = R.tableId;
   DELETE FROM table WHERE tableId = R.tableid;
   /* More child tables here */
 NEXT

По-друге, коли ви додаєте в додаткову дочірню таблицю з каскадним видаленням, код у випадку 1 продовжує працювати.

Я б лише помістив у каскад, де семантика відносин є "частиною". Інакше якийсь ідіот видалить половину вашої бази даних, коли ви це зробите:

DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'

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

2

Я намагаюся уникати видалень або оновлень, які я не вимагав прямо на SQL-сервері.

Або через каскад, або через використання тригерів. Вони, як правило, кусають тебе в попу деякий час вниз, або при спробі відстежити помилку, або при діагностуванні проблем з продуктивністю.

Де я б їх використав, це гарантувати послідовність не дуже великих зусиль. Для отримання такого ж ефекту вам доведеться використовувати збережені процедури.


2

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

Єдиний випадок, коли я використовую, якщо дані таблиці таблиці надзвичайно контрольовані (наприклад, обмежені дозволи) і лише оновлюються або видаляються з допомогою контрольованого процесу (наприклад, оновлення програмного забезпечення), яке було перевірено.


1

Видалення або оновлення до S, яке видаляє значення зовнішнього ключа, знайдене в деяких кортежах R, може оброблятися одним із трьох способів:

  1. Відхилення
  2. Розмноження
  3. анулювання.

Розмноження називають каскадним.

Є два випадки:

‣ Якщо кортеж у S був видалений, видаліть R-кортежі, на які він посилався.

‣ Якщо кортеж S був оновлений, оновіть значення в R кортежів, які посилаються на нього.


0

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

Щойно я запровадив видалення каскаду для нової таблиці перетину між двома вже існуючими таблицями (перетин лише для видалення), після того як видалення каскаду було відмовлено від досить тривалого часу. Також не так вже й погано, якщо дані втрачаються.

Однак у таблицях списку, подібних до перерахунків, погано: хтось видаляє запис 13 - жовтий із таблиці "кольори", а всі жовті елементи з бази видаляються. Крім того, вони інколи оновлюються способом видалення-все-вставлення-все, що призводить до того, що референтна цілісність повністю пропущена. Звичайно, це неправильно, але як ви зміните складне програмне забезпечення, яке працює вже багато років, при цьому впровадження справжньої референтної цілісності загрожує несподіваними побічними ефектами?

Ще одна проблема полягає в тому, коли оригінальні значення іноземного ключа зберігаються навіть після видалення первинного ключа. Можна створити стовпчик надгробних плит і параметр ON DELETE SET NULL для вихідного FK, але для цього знову потрібні тригери або специфічний код, щоб підтримувати зайве (крім випадків після видалення PK) значення ключа.


0

Каскадні видалення надзвичайно корисні при впровадженні логічних логічних супер-типів та підтипів у фізичній базі даних.

Коли окремі таблиці типів супертипів і підтипів використовуються для фізичної реалізації супертипів / підтипів (на відміну від згортання всіх атрибутів підтипів до однієї фізичної таблиці супертипів), існує один -один зв'язок між цими таблицями та проблемою стає тоді, як зберегти первинні ключі на 100% синхронізацією між цими таблицями.

Каскадні видалення можуть бути дуже корисним інструментом для:

1) Переконайтесь, що при видаленні запису супер-типу також видаляється відповідна одиночна запис підтипу.

2) Переконайтесь, що будь-яке видалення запису підтипу також видаляє запис супертипу. Це досягається реалізацією тригера видалення "замість" на таблиці підтипів, що йде, та видаляє відповідну запис супер-типу, яка, в свою чергу, каскад видаляє запис підтипу.

Використання каскадного видалення таким чином гарантує відсутність записів осиротілих надтипових чи підтипових типів незалежно від того, чи ви видалите спочатку запис супер-типу або запис підтипу.


Хороший приклад. У JPA це таблиця приєднаних InheritanceStrategy. Для 1): Зазвичай ви використовуєте структуру шару стійкості (EclipseLink, Hibernate, ...), яка реалізує видалену послідовність для об'єднаного об'єкта, щоб спочатку видалити об'єднану частину, а потім супер частину. Але якщо у вас є більш базове програмне забезпечення, як, наприклад, імпорт або архівне завдання, зручно мати можливість просто видалити об'єкт, видавши видалення в супер-частині. Щодо 2): погоджуйтесь, але в такому випадку клієнт повинен уже знати, що він працює над об'єднаною / підроздільною частиною організації.
лео
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.