Что произойдёт с проверкой ссылочной целостности, если значение внешнего ключа равно NULL?

Что произойдёт с проверкой ссылочной целостности, если значение внешнего ключа равно NULL?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

При обычной семантике SQL значение NULL во внешнем ключе означает отсутствие ссылки, поэтому соответствующая строка родительской таблицы не требуется. Ограничение внешнего ключа не нарушается, если столбец допускает NULL.

Это не означает, что NULL совпадает с каким-либо ключом: NULL не равен ни одному значению, включая другой NULL.

Исторический контекст

Реляционные базы данных должны контролировать ссылки между отношениями, но не каждая запись обязана иметь связанную запись. Например, заказ может быть создан без назначенного клиента, а задача — без исполнителя.

Внешний ключ решает проблему ссылочной целостности, а NULL позволяет выразить необязательную связь. Отдельное ограничение NOT NULL используется, если связь должна быть обязательной.

Постановка проблемы

Предположим, дочерняя таблица содержит внешний ключ на родительскую. Если внешний ключ равен NULL, попытка найти родительскую строку по этому значению не должна считаться ошибкой: ссылка фактически не задана.

Риск возникает, когда разработчик принимает допустимость NULL за обязательность связи. Тогда база пропускает строки без родителя, хотя бизнес-правило может требовать, чтобы ссылка существовала всегда.

Подробное решение

Для внешнего ключа из одного столбца NULL обычно означает отсутствие значения ссылки. Поэтому проверка наличия соответствующей строки в родительской таблице не применяется, и вставка или обновление принимается, если остальные ограничения не нарушены.

CREATE TABLE client ( id INTEGER PRIMARY KEY ); CREATE TABLE orders ( id INTEGER PRIMARY KEY, client_id INTEGER REFERENCES client(id) ); INSERT INTO orders (id, client_id) VALUES (1, NULL);

В примере последняя вставка допустима: столбец client_id не объявлен как NOT NULL. Чтобы каждый заказ обязан был ссылаться на клиента, нужны одновременно внешний ключ и запрет NULL: внешний ключ проверяет существование клиента, а NOT NULL — наличие самого значения.

Для составного внешнего ключа важна выбранная стандартом семантика сопоставления. При обычном MATCH SIMPLE наличие NULL хотя бы в одной ссылочной колонке делает проверку внешнего ключа неприменимой; это может позволить частично заполненную ссылку. MATCH FULL требует, чтобы либо все компоненты были NULL, либо все были заполнены и соответствовали одной родительской строке.

NULL во внешнем ключе не является специальным значением, которое можно найти в родительском ключе. Это маркер отсутствующей ссылки, а не идентификатор. Конкретные дополнительные ограничения, например CHECK, могут запретить частично заполненные составные ссылки или реализовать более строгие бизнес-правила.

Ситуация из практики

В системе заказов client_id допускает NULL, потому что заказы иногда принимаются до регистрации клиента. Команда обнаружила, что часть заказов остаётся без владельца, и рассмотрела два варианта: искать клиента приложением или сделать связь обязательной на уровне базы.

Проверка только в приложении имела плюс — гибче поддерживала разные сценарии, но минусом была уязвимость к другим сервисам и ручным операциям. Добавление NOT NULL сразу защищало целостность, но не подходило для исторических и ещё не идентифицированных заказов.

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

Что кандидаты часто упускают

  1. Означает ли NULL во внешнем ключе, что в родительской таблице должна существовать строка с NULL?

Нет. Внешний ключ с NULL не ссылается на строку родительской таблицы. Для него проверка соответствия ключу не выполняется; кроме того, первичный ключ обычно сам не допускает NULL.

  1. Достаточно ли внешнего ключа, чтобы запретить отсутствие родительской записи?

Нет. Внешний ключ запрещает несуществующие ненулевые ссылки, но обычно допускает NULL. Для обязательной связи нужно дополнительно объявить ссылочный столбец NOT NULL; иначе строка без родителя останется допустимой.

  1. Как изменится поведение составного внешнего ключа при частично заполненной ссылке?

При MATCH SIMPLE, являющемся обычным вариантом, NULL хотя бы в одной компоненте обычно освобождает строку от проверки ссылки. При MATCH FULL допустимы только два состояния: все компоненты NULL или все заполнены и образуют существующий ключ. Поэтому для составных связей MATCH SIMPLE может пропустить логически бессмысленные комбинации, если их отдельно не ограничить.