В миграции взаимосвязанные строки нужно временно вставить в порядке, нарушающем внешний ключ. Какой механизм SQL позволяет отложить проверку ограничения до конца транзакции?
Для этого внешний ключ объявляют как откладываемое ограничение — DEFERRABLE, обычно с режимом INITIALLY DEFERRED. Тогда СУБД проверяет ссылочную целостность не после каждой операции, а при фиксации транзакции.
Если к моменту COMMIT условие нарушено, транзакция завершается ошибкой. Это не отключает проверку, а только переносит момент её выполнения.
Внешние ключи появились для автоматической защиты ссылочной целостности между таблицами. При немедленной проверке каждая вставка или модификация должна быть корректной сразу после выполнения оператора.
Такой режим неудобен при загрузке взаимосвязанных данных, циклических ссылках и операциях переноса записей. Отложенная проверка позволяет выполнить согласованный набор изменений внутри одной транзакции, не требуя правильного промежуточного состояния после каждой команды.
Предположим, одна строка ссылается на другую, но сначала необходимо создать зависимую строку, а родительскую — позже. При немедленной проверке первая операция будет отклонена, хотя итоговое состояние после всей миграции должно быть корректным.
Опасно путать отложенную проверку с отключением ограничения. При отключении целостность вообще может не контролироваться, а при DEFERRABLE она обязательно проверяется в установленный момент — обычно перед COMMIT.
Ограничение DEFERRABLE может находиться в одном из двух начальных режимов:
INITIALLY IMMEDIATE — проверка выполняется после каждого оператора, но режим можно отложить внутри транзакции;INITIALLY DEFERRED — проверка откладывается до завершения транзакции.Минимальный пример:
Первая вставка временно ссылается на отсутствующую строку, но к моменту COMMIT родительская строка существует. Если COMMIT не выполнится, например из-за нарушения внешнего ключа, вся транзакция не считается успешно завершённой.
Режим можно изменить внутри транзакции для ограничений, объявленных как DEFERRABLE. Не каждое ограничение допускает отложенную проверку: в распространённых СУБД NOT NULL и CHECK обычно проверяются немедленно, а поддержка и синтаксис отдельных возможностей зависят от конкретной СУБД.
Компромисс состоит в том, что ошибка обнаруживается позже. Это усложняет диагностику и может удерживать больше промежуточных данных до фиксации транзакции, зато позволяет атомарно выполнить операции, которые невозможно корректно упорядочить при немедленной проверке.
При миграции данных из старой модели в новую обнаружились циклические ссылки: запись отдела ссылалась на руководителя, а запись руководителя принадлежала этому же отделу. Вставка в строгом порядке была невозможна.
Рассматривались три варианта. Временно отключить внешний ключ было проще, но это создавало риск незаметно сохранить некорректные ссылки. Сделать внешние ключи допускающими NULL позволяло загрузку, но требовало дополнительных обновлений и ослабляло модель. Использовать отложенные внешние ключи оказалось безопаснее: все строки загружались в одной транзакции, а целостность проверялась перед фиксацией.
Выбранный вариант сохранил автоматическую проверку и сделал ошибку атомарной: либо миграция полностью согласована, либо изменения не принимаются. Для обычных независимых операций оставили немедленную проверку, чтобы ошибки выявлялись как можно раньше.
Нет. Отложенным может быть только ограничение, созданное с возможностью DEFERRABLE. Если оно объявлено как немедленное и неоткладываемое, смена режима транзакции его не изменит. Обычно требуется заменить ограничение через ALTER TABLE, причём конкретная поддержка такого изменения зависит от СУБД.
COMMIT?Промежуточное нарушение допустимо, если ограничение действительно отложено. СУБД должна увидеть корректное состояние в момент проверки, обычно при COMMIT; если к этому моменту все ссылки действительны, транзакция может завершиться успешно.
Это не означает, что любые операции разрешены: например, ошибка типов, нарушение NOT NULL или синтаксическая ошибка обычно возникает немедленно.
INITIALLY IMMEDIATE, если ограничение иногда требуется откладывать?Такой режим сохраняет раннее обнаружение ошибок в обычных транзакциях. Отложить проверку можно только в специально предусмотренных сценариях, например при массовой миграции или обработке циклических зависимостей.
Это компромисс между безопасностью по умолчанию и гибкостью. Режим INITIALLY DEFERRED удобнее для сложных загрузок, но переносит диагностику ошибок к концу транзакции и может затруднить поиск операции, которая создала проблему.