Primary key vs natural key

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это различать

На собеседовании по SQL или data modeling часто спрашивают: «какой primary key выбрать — auto-increment или email?». Это вопрос о surrogate (искусственном) vs natural (естественном) ключе. От выбора зависит performance, readability, и возможность рефакторинга бизнес-логики.

В классических учебниках говорят «используйте natural key, если существует». В реальности 95% баз — surrogate. Знать обе стороны — базовый навык аналитика / data engineer.

В статье:

  • Короткий ответ и определения
  • Плюсы и минусы каждого подхода
  • Примеры из реальной жизни
  • Composite keys как компромисс
  • Best practices 2026

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

  • Surrogate key (PK) — искусственный идентификатор (auto-increment INT, UUID). Не несёт бизнес-смысла.
  • Natural key — значение из бизнес-домена (email, username, ИНН, VIN).

В большинстве современных систем — surrogate. Natural — для справочников и legacy.

Surrogate key

CREATE TABLE users (
    id SERIAL PRIMARY KEY,     -- surrogate
    email VARCHAR(255) UNIQUE,
    name VARCHAR(100)
);

Плюсы

  • Стабильность: не меняется, даже если email изменится
  • Компактный: 4-8 байт (INT/BIGINT), лучше чем длинная строка
  • Быстрые JOIN: integer comparison быстрее string
  • Приватность: в URL / логах не светит email
  • Независимость от бизнеса: бизнес-правила меняются, ID нет

Минусы

  • Нет семантики
  • Нужен дополнительный UNIQUE index на бизнес-ключ
  • Легко случайно создать дубли (с разными ID, но тем же email)

Natural key

CREATE TABLE countries (
    code CHAR(2) PRIMARY KEY,  -- natural: 'RU', 'US'
    name VARCHAR(100)
);

Плюсы

  • Читаемость: сразу понятно, что RU — Россия
  • Не нужен дополнительный lookup
  • Меньше таблиц (для справочников)

Минусы

  • Мутабельность: бизнес может поменять код
  • Длиннее: string vs int
  • JOIN медленнее: string comparison
  • Приватность: если это email / VIN — утекает в логи

Пример плохого natural key

Email как PK users:

CREATE TABLE users (
    email VARCHAR(255) PRIMARY KEY,  -- плохая идея
    ...
);
CREATE TABLE orders (
    user_email VARCHAR(255) REFERENCES users(email),  -- FK на email
    ...
);

Проблема: пользователь меняет email — нужно обновить FK везде. CASCADE UPDATE теоретически работает, но на миллионах записей — боль.

С surrogate:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE  -- меняется свободно
);
CREATE TABLE orders (
    user_id INT REFERENCES users(id)  -- стабильный FK
);

Типичные surrogate keys

Auto-increment INT / BIGINT

id SERIAL PRIMARY KEY          -- Postgres
id INT AUTO_INCREMENT PRIMARY KEY  -- MySQL

Плюсы: компактный, readable в URL (/user/42). Минусы: гадкой в distributed системах (collision).

UUID

id UUID PRIMARY KEY DEFAULT gen_random_uuid()

Плюсы: distributed-safe, не светит порядок created. Минусы: 16 байт, slow на индексе (random).

Snowflake / ULID

Временно-упорядоченные ID. Компромисс между INT и UUID.

Composite keys

Комбинация нескольких полей:

CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    qty INT,
    PRIMARY KEY (order_id, product_id)  -- composite
);

Для junction-таблиц и history-таблиц. Естественно: один product в одном order_id встречается один раз.

Best practices

1. Default — surrogate (auto-increment INT или UUID)

Для 95% таблиц.

2. Natural key как UNIQUE constraint

Гарантирует уникальность email без использования его как PK:

email VARCHAR(255) UNIQUE NOT NULL

3. Справочники — natural key ok

countries с PK code CHAR(2) — нормально. Значения стабильны.

4. Distributed системы — UUID

Если данные создаются в разных регионах / системах.

На собесе

«Surrogate или natural key?» Surrogate по default. Natural — для справочников с immutable кодами.

«Email PK — почему плохо?» Email меняется. Все FK ломаются или CASCADE UPDATE.

«UUID или INT?» INT default. UUID в distributed / privacy-sensitive.

«Что такое composite key?» PK из нескольких колонок. Для junction table.

Частые ошибки

1. Использовать mutable natural key

Email, phone, username — могут меняться. Плохой PK.

2. Использовать password как natural id

Никогда. В PK вообще не должно быть sensitive данных.

3. Не добавить UNIQUE на natural-like column

Если email не PK, но должен быть уникальным — UNIQUE обязателен.

4. Смешивать подходы в проекте

Одна таблица — natural, другая — surrogate. Консистентность лучше.

Связанные темы

FAQ

UUID замедляет?

На B-tree индексе — да (random inserts). ULID / sequential UUID v7 решают.

Auto-increment collision в sharding?

Да, поэтому в distributed — UUID или ID generator.

Можно PK изменить?

Технически — да. На практике — риск сломать FK и migrations. Почти не делают.

Surrogate всегда INT?

Исторически. Сейчас UUID популярны для microservices.


Тренируйте data modeling — откройте тренажёр с 1500+ вопросами для собесов.