Когда и зачем использовать каскадирование в SQL Server? - PullRequest
141 голосов
/ 12 сентября 2008

При настройке внешних ключей в SQL Server, при каких обстоятельствах он должен каскадироваться при удалении или обновлении и каковы причины этого?

Это, вероятно, относится и к другим базам данных.

Больше всего я ищу конкретные примеры каждого сценария, желательно от того, кто их успешно использовал.

Ответы [ 15 ]

121 голосов
/ 12 сентября 2008

Краткое изложение того, что я видел до сих пор:

  • Некоторым людям совсем не нравится каскадирование.

Каскад Удалить

  • Каскадное удаление может иметь смысл, когда семантика отношений может включать исключительное "является частью " описания. Например, запись OrderLine является частью своего родительского заказа, и OrderLines никогда не будут разделены между несколькими заказами. Если бы ордер исчез, то OrderLine также должен быть, и строка без ордера будет проблемой.
  • Каноническим примером для Каскадного удаления является SomeObject и SomeObjectItems, где не имеет смысла когда-либо существующая запись элементов без соответствующей основной записи.
  • Вы должны , а не использовать Каскадное удаление, если вы сохраняете историю или используете «мягкое / логическое удаление», когда вы устанавливаете только столбец удаленных битов в 1 / true.

Каскадное обновление

  • Каскадное обновление может иметь смысл, когда вы используете реальный ключ, а не суррогатный ключ (столбец идентификатора / автоинкремента) в таблицах.
  • Канонический пример для Cascade Update - это когда у вас есть изменяемый внешний ключ, например, имя пользователя, которое можно изменить.
  • Вы должны , а не использовать Каскадное обновление с ключами, которые являются столбцами Identity / autoincrement.
  • Каскадное обновление лучше всего использовать в сочетании с уникальным ограничением.

Когда использовать каскад

  • Возможно, вы захотите получить дополнительное строгое подтверждение от пользователя, прежде чем разрешить каскадную операцию, но это зависит от вашего приложения.
  • Каскадирование может привести к неприятностям, если вы неправильно настроили свои внешние ключи. Но с тобой все будет в порядке, если ты все сделаешь правильно.
  • Неразумно использовать каскадирование, прежде чем полностью его понять. Однако это полезная функция, и поэтому стоит потратить время на ее понимание.
64 голосов
/ 12 сентября 2008

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

Что плохо, так это неправильное использование внешних ключей, например, их создание в обратном направлении.

Пример Хуана Мануэля является каноническим примером, если вы используете код, есть гораздо больше шансов оставить поддельные DocumentItems в базе данных, которая придет и укусит вас.

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

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

17 голосов
/ 12 сентября 2008

Я никогда не использую каскадные удаления.

Если я хочу, чтобы что-то было удалено из базы данных, я хочу явно указать базе данных, что я хочу удалить.

Конечно, они являются функцией, доступной в базе данных, и могут быть случаи, когда их можно использовать, например, если у вас есть таблица 'order' и таблица 'orderItem', вы можете очистить элементы, когда Вы удаляете заказ.

Мне нравится ясность, которую я получаю от выполнения этого в коде (или хранимой процедуре), а не от "волшебства".

По той же причине я тоже не фанат триггеров.

Следует обратить внимание на то, что если вы удалите «заказ», вы получите отчет «На 1 строку затронут», даже если каскадное удаление удалило 50 «элементов OrderItem».

12 голосов
/ 16 сентября 2008

Я много работаю с каскадным удалением.

Приятно знать, что тот, кто работает с базой данных, никогда не оставит никаких нежелательных данных. Если зависимости растут, я просто изменяю ограничения на диаграмме в Management Studio, и мне не нужно настраивать sp или dataacces.

Тем не менее, у меня есть 1 проблема с каскадным удалением, и это циклические ссылки. Это часто приводит к частям базы данных, которые не имеют каскадных удалений.

10 голосов
/ 12 сентября 2008

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

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

В общем, я вообще избегаю реального удаления и использую вместо этого логические удаления (т. Е. Наличие битового столбца с именем isDeleted, который устанавливается в значение true).

9 голосов
/ 12 сентября 2008

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

5 голосов
/ 12 января 2014

ON Удалить каскад:

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

Если при каскадном удалении не используется, то возникнет ошибка для ссылочной целостности .

ВКЛ Каскад обновления:

Когда вы хотите изменить первичный ключ , который будет обновлен в внешний ключ

5 голосов
/ 01 марта 2013

Используйте каскадное удаление там, где вы хотите удалить запись с FK, если удалена соответствующая запись PK. Другими словами, где запись не имеет смысла без ссылки на запись.

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

3 голосов
/ 23 июня 2014

Я слышал о администраторах баз данных и / или "Политике компании", которые запрещают использование "Каскадного удаления" (и других) исключительно из-за плохого опыта в прошлом. В одном случае парень написал три триггера, которые в итоге вызвали друг друга. Три дня восстановления привели к полному запрету триггеров, все из-за действий одного idjit.

Конечно, иногда необходимы триггеры вместо «Каскад при удалении», например, когда необходимо сохранить некоторые дочерние данные. Но в других случаях вполне допустимо использовать каскадный метод при удалении. Основное преимущество «Каскада при удалении» заключается в том, что он захватывает ВСЕ дочерние элементы; Пользовательская письменная процедура триггера / сохранения может не работать, если она не закодирована правильно.

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

Разве не в этом вся суть развития?

3 голосов
/ 19 октября 2008

Одной из причин использовать каскадное удаление (а не делать это в коде) является повышение производительности.

Случай 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'
...