Столбец объявлен уникальным: почему это ещё не делает его первичным ключом?

Столбец объявлен уникальным: почему это ещё не делает его первичным ключом?

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

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

Уникальность и первичный ключ решают разные задачи. Ограничение UNIQUE запрещает дублирование допустимых значений, но обычно допускает NULL и не требует, чтобы столбец был единственным главным идентификатором строк. Первичный ключ одновременно обеспечивает уникальность, запрет NULL и задаёт основной способ идентификации строк в отношении.

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

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

SQL разделяет эти роли: PRIMARY KEY обозначает основной ключ отношения, а UNIQUE позволяет объявлять альтернативные кандидатные ключи или бизнес-ограничения. Такое разделение поддерживает одновременно техническую идентификацию строки и контроль уникальности предметных данных.

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

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

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

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

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

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

CREATE TABLE customer ( customer_id BIGINT PRIMARY KEY, email VARCHAR(320) UNIQUE, display_name VARCHAR(200) NOT NULL ); INSERT INTO customer VALUES (1, NULL, 'Иван'); INSERT INTO customer VALUES (2, NULL, 'Ольга');

Здесь customer_id обязан быть уникальным и непустым. email не может содержать два одинаковых известных значения, но две строки с NULL обычно допустимы.

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

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

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

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

Вариант с одним UNIQUE на email прост и допускает отсутствие адреса, но не подходит как основной идентификатор: NULL не идентифицирует клиента, а изменение email затрагивает бизнес-данные. Вариант с email как PRIMARY KEY запрещает клиентов без email и связывает техническую модель с изменяемым атрибутом.

Рациональное решение — отдельный неизменяемый customer_id как PRIMARY KEY и дополнительное UNIQUE-ограничение на email. В результате внешние ссылки используют стабильный ключ, а уникальность известных адресов сохраняется без запрета регистрации клиентов без email.

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

  1. Может ли UNIQUE-ограничение гарантировать ровно одно NULL-значение?

Нет, обычное UNIQUE-ограничение для nullable-столбца обычно не предназначено для такого правила. Несколько NULL, как правило, допустимы, потому что NULL означает отсутствие известного значения, а не конкретное значение, равное другому NULL.

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

  1. Почему кандидатный ключ может быть UNIQUE, но не PRIMARY KEY?

У отношения может существовать несколько минимальных наборов атрибутов, однозначно идентифицирующих строки. Все такие наборы являются кандидатными ключами, но один из них выбирают в качестве первичного, а остальные объявляют альтернативными ключами через UNIQUE.

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

  1. Всегда ли UNIQUE разрешает внешнему ключу ссылаться на его столбец?

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

Составной уникальный ключ нельзя заменить уникальностью отдельных его компонентов: уникальность пары «организация — номер» не означает уникальность одного номера во всей таблице. Поэтому внешний ключ должен ссылаться на полный совместимый набор столбцов, иначе ссылочная целостность не будет однозначной.