Как обеспечить уникальность бизнес идентификатора, если первичный ключ таблицы — искусственный?

Как обеспечить уникальность бизнес-идентификатора, если первичный ключ таблицы — искусственный?

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

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

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

Если значение обязательно, его обычно дополняют ограничением NOT NULL. Так база данных сама защищает правило предметной области независимо от приложения и параллельных транзакций.

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

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

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

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

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

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

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

Бизнес-идентификатор нужно объявить альтернативным ключом через UNIQUE. Если он обязателен, следует явно запретить отсутствие значения через NOT NULL.

CREATE TABLE users ( id BIGINT PRIMARY KEY, email VARCHAR(320) NOT NULL UNIQUE );

В этом примере id является техническим первичным ключом, а email — уникальным бизнес-идентификатором. СУБД создает механизм проверки уникальности и отклоняет вставку или обновление, нарушающее это правило.

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

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

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

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

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

Рассматривались три варианта. Оставить проверку в приложении было проще, но она не защищала от гонок. Считать email первичным ключом обеспечивало бы уникальность, однако усложняло бы изменение email и увеличивало бы связанность внешних ключей с бизнес-правилом. Добавить отдельное ограничение NOT NULL UNIQUE сохранило стабильный технический идентификатор и передало контроль целостности базе.

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

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

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

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

Обычно оставляют один первичный ключ для стабильной идентификации записи, а альтернативные бизнес-идентификаторы оформляют через UNIQUE. Это разделяет техническую идентификацию и правила предметной области.

  1. Что произойдет с ограничением UNIQUE при изменении бизнес-идентификатора?

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

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

  1. Всегда ли UNIQUE запрещает несколько строк с отсутствующим значением?

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

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