Какое базовое правило целостности нарушает составной первичный ключ с допускающим NULL компонентом?

Какое базовое правило целостности нарушает составной первичный ключ с допускающим NULL компонентом?

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

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

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

NULL означает не конкретное значение, а его отсутствие или неизвестность. Поэтому строка с NULL в части первичного ключа не имеет однозначного идентификатора, даже если остальные компоненты заполнены.

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

В реляционной модели первичный ключ используется для идентификации кортежа, то есть строки отношения. Требование, чтобы ключ был полностью определён, называют целостностью сущности.

Это правило отделяет идентификатор строки от обычного признака. Для обычного столбца отсутствие значения может быть допустимо, но идентификатор не может быть неизвестным: иначе невозможно надёжно ссылаться на строку и отличать её от других.

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

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

Попытка заменить первичный ключ уникальным ограничением не решает проблему. Уникальность отвечает за отсутствие совпадающих известных значений, а не за обязательную определённость идентификатора. Кроме того, обработка NULL в уникальных ограничениях может различаться по СУБД.

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

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

СУБД должна объявлять все столбцы первичного ключа обязательными. Для составного ключа это означает запрет NULL в каждом его компоненте, а не только в комбинации целиком.

CREATE TABLE employee ( department_id INTEGER NOT NULL, employee_no INTEGER NOT NULL, name VARCHAR(100) NOT NULL, PRIMARY KEY (department_id, employee_no) );

В этом примере строка идентифицируется парой (department_id, employee_no). Обе части должны быть заданы, а повторение всей пары запрещено.

UNIQUE и PRIMARY KEY решают разные задачи. Уникальное ограничение запрещает дублирование значений согласно правилам конкретной СУБД, но обычно допускает NULL; первичный ключ одновременно обеспечивает уникальность и обязательность всех ключевых столбцов.

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

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

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

В кадровой системе сотрудник временно импортируется до определения его подразделения. Рассматривались два варианта: разрешить NULL в составном первичном ключе или создать отдельный идентификатор сотрудника.

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

Выбрали отдельный обязательный идентификатор сотрудника, а пару подразделение—номер сделали отдельным уникальным ограничением, допускающим заполнение только после завершения импорта. Это сохранило надёжные ссылки и позволило безопасно обрабатывать неполные входные данные.

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

1. Может ли уникальное ограничение полностью заменить первичный ключ?

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

2. Что происходит с внешним ключом, ссылающимся на составной ключ?

Внешний ключ должен содержать соответствующие компоненты в том же порядке и ссылаться на уникальный ключ родительской таблицы. Если часть внешнего ключа NULL, поведение зависит от правил внешних ключей и конкретной СУБД, но такая строка обычно рассматривается как не полностью заданная ссылка; это не превращает NULL в идентификатор родительской строки.

3. Зачем отдельно задавать уникальность бизнес-ключа при наличии искусственного первичного ключа?

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