В практической ситуации удаляют запись родителя, на которую ссылаются дочерние строки: почему внешний ключ сам по себе не определяет дальнейшую судьбу этих строк?
Внешний ключ задаёт условие ссылочной целостности, но не обязательно задаёт действие при удалении родительской строки. Если действие явно не определено, обычно применяется запрет удаления при наличии зависимых строк, то есть поведение, эквивалентное NO ACTION; точное поведение зависит от СУБД и объявления ограничения.
Дальнейшая судьба дочерних строк определяется referential action: CASCADE, SET NULL, SET DEFAULT или запретом операции. Выбор должен соответствовать смыслу данных, иначе удаление может либо оставить «сироты», либо неожиданно уничтожить историю.
Ссылочная целостность появилась как средство защиты связей между отношениями: значение внешнего ключа должно ссылаться на существующую строку родительского отношения либо быть допустимым NULL. Одной проверки существования родителя недостаточно для операции удаления, потому что удаление может нарушить это условие сразу для многих дочерних строк.
Реляционная модель отделяет ограничение данных от конкретной бизнес-операции. Поэтому правило удаления задаётся отдельно: база данных не может универсально угадать, нужно ли удалить зависимые данные, отвязать их или сохранить родителя.
Пусть заказ содержит внешний ключ на клиента. При удалении клиента возможны разные требования:
Неверный выбор приводит к разным рискам. CASCADE может удалить значительный объём истории, а SET NULL может разрушить обязательную для отчётности связь. Простое отключение ограничения ещё опаснее: оно допускает строки, для которых родитель не существует.
Внешний ключ состоит из двух частей: проверки допустимости ссылок и правила реакции на изменение ключа родителя. Например, ограничение с ON DELETE CASCADE удаляет дочерние строки при удалении родительской строки, а ON DELETE SET NULL устанавливает внешний ключ в NULL; последний вариант возможен только для nullable-столбца.
Минимальный пример:
После удаления клиента его заказы сохраняются, но теряют прямую ссылку на клиента. Это подходит, если заказ является самостоятельным историческим фактом; для данных, которые не имеют смысла без родителя, обычно выбирают запрет удаления или каскадное удаление.
NO ACTION означает, что итоговая операция не должна нарушить ссылочную целостность. В некоторых СУБД он проверяется в конце инструкции или транзакции, особенно при отложенных ограничениях. RESTRICT, если поддерживается, обычно требует немедленной проверки; конкретные различия и доступность отложенных ограничений зависят от СУБД.
При обновлении ключа родителя действуют аналогичные правила через ON UPDATE. На практике первичные ключи обычно не меняют, поэтому главным источником последствий остаётся удаление.
Каскадные действия должны быть предсказуемыми по цепочке: удаление одной строки может затронуть зависимые таблицы дальше. Для аудита и финансовых данных часто предпочитают логическое удаление или запрет физического удаления, потому что каскад не оставляет автоматически восстановимой истории.
В системе заказов клиент может запросить удаление персональных данных, но сами заказы должны сохраняться для бухгалтерского учёта. Вариант с CASCADE прост, однако уничтожает документы и нарушает требования к хранению истории. Вариант с запретом удаления сохраняет целостность, но не позволяет удалить персональные сведения.
Вариант SET NULL сохраняет заказы, но требует, чтобы отчёты корректно обрабатывали отсутствие клиента. Более подходящим решением может быть обезличивание клиента с сохранением технической записи или перенос ссылки на специальную обезличенную сущность; это позволяет сохранить обязательность связи и отделить историю заказа от персональных данных.
Выбор фиксируют в модели данных и проверяют тестами удаления. Результат должен быть явным: удаление клиента не должно случайно запускать каскад по заказам, платежам и документам.
Да, если столбец внешнего ключа не объявлен как NOT NULL. NULL обычно означает отсутствие связи, а не ссылку на несуществующего родителя; поэтому такая строка не нарушает ссылочную целостность. Если связь обязательна по бизнес-правилу, одного внешнего ключа недостаточно — нужен также NOT NULL.
Можно, но результат зависит от всех столбцов, входящих во внешний ключ. Для корректного отвязывания СУБД должна установить NULL в соответствующие дочерние столбцы, и каждый из них должен допускать NULL. Частично заполненная составная ссылка может иметь специальную семантику, зависящую от правил конкретной СУБД, поэтому такие схемы нужно проверять отдельно.
Каскад — декларативное поведение самого ограничения ссылочной целостности: СУБД знает, что действие является частью поддержания связи и выполняет его в рамках операции. Триггер — произвольная программируемая логика, способная выполнить дополнительные действия, но её сложнее анализировать, тестировать и ограничивать.
Триггер может быть нужен для аудита или сложного бизнес-процесса, однако использовать его как замену обычному каскаду обычно невыгодно. Чем больше скрытой логики при удалении, тем труднее предсказать затронутые строки и безопасно проводить миграции.