Primary key vs foreign key: разница

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

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

  • Primary key (PK) — уникальный идентификатор строки в таблице. Нельзя NULL, нельзя дубли.
  • Foreign key (FK) — ссылка из одной таблицы на PK другой. Обеспечивает связь между таблицами.

PK говорит: «Я — ключ этой таблицы». FK говорит: «Я — указатель на чью-то другую PK».

Пример

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

CREATE TABLE orders (
    order_id INT PRIMARY KEY,         -- PK для orders
    user_id INT REFERENCES users(id), -- FK на users.id
    total DECIMAL(10, 2),
    created_at TIMESTAMP
);
  • users.id — primary key таблицы users
  • orders.order_id — primary key таблицы orders
  • orders.user_id — foreign key, ссылается на users.id

Primary key подробнее

Свойства PK

  • Уникальность: нет двух строк с одинаковым значением
  • NOT NULL: значение обязательно
  • Immutable: в хороших практиках не меняется после создания
  • Индекс: автоматически создаётся уникальный индекс

Один столбец или несколько (composite key)

-- одна колонка
PRIMARY KEY (id)

-- composite (несколько колонок)
PRIMARY KEY (order_id, product_id)

Типичные варианты

  • Auto-increment integer: id SERIAL PRIMARY KEY (Postgres) или id INT AUTO_INCREMENT PRIMARY KEY (MySQL)
  • UUID: для распределённых систем
  • Natural key: email как PK (риск: email может меняться)

Foreign key подробнее

Свойства FK

  • Значение должно существовать в referenced таблице (или быть NULL)
  • Может быть NULL (если столбец позволяет)
  • Не обязательно уникальный (обычно один user → много orders)
  • Автоматически НЕ создаёт индекс (рекомендуется создавать вручную для performance)

Создание

-- inline
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT REFERENCES users(id)
);

-- named constraint
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT,
    CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
);

-- добавить к существующей таблице
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id);

ON DELETE / ON UPDATE

Что делать, когда удаляют / обновляют строку в referenced таблице:

FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE       -- удалить дочерние
    ON UPDATE CASCADE       -- обновить дочерние

-- или
    ON DELETE SET NULL      -- поставить NULL
    ON DELETE SET DEFAULT   -- поставить DEFAULT
    ON DELETE RESTRICT      -- запретить удаление (default)
    ON DELETE NO ACTION     -- то же, что RESTRICT

Главные отличия

Primary Key Foreign Key
Уникальность обязательна не требуется
NULL нельзя можно
Количество в таблице ровно 1 (составной или нет) сколько угодно
Назначение уникальный идентификатор связь между таблицами
Индекс автоматически нужно создать вручную
Referential integrity сам по себе не обеспечивает обеспечивает

Зачем всё это аналитику

Аналитик обычно не создаёт таблицы, но:

  1. Понимает структуру БД — какая таблица на какую ссылается, как джойнить
  2. Пишет JOIN — на FK
  3. Проверяет данные — сироты (orphan records) — строки с FK, которые ссылаются в никуда
  4. Отличает уникальные сущности от связей — users (PK = id) vs orders (PK = order_id, FK = user_id)

Пример: проверка целостности

Найти orders, которые ссылаются на несуществующих users:

SELECT o.*
FROM orders o
LEFT JOIN users u ON u.id = o.user_id
WHERE u.id IS NULL;

Если в БД есть FK — таких строк быть не должно. Если есть — или FK выключен, или баг при insert.

Composite primary key

Используется, когда уникальность обеспечивается сочетанием колонок:

CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    qty INT,
    PRIMARY KEY (order_id, product_id),       -- composite PK
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

Каждая пара (order_id, product_id) уникальна: один заказ не может содержать один и тот же товар дважды.

Surrogate vs natural key

  • Natural key — реальная характеристика (email, СНИЛС)
  • Surrogate key — искусственный (auto-increment id)

Обычно предпочитают surrogate: natural могут меняться (человек меняет email).

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

Ошибка 1. FK без индекса

Без индекса на FK-колонку JOIN работает медленно. Создавайте индекс:

CREATE INDEX idx_orders_user_id ON orders(user_id);

Ошибка 2. Забывать FK

Без FK в БД оказываются orphan-records. JOIN даёт NULL, статистика врёт.

Ошибка 3. CASCADE DELETE без понимания

Удаление пользователя удаляет его заказы → потеря финансовых данных. Обычно для финансов — ON DELETE RESTRICT или soft-delete.

Ошибка 4. Несколько PK на таблицу

Невозможно технически. Composite PK — это ОДИН PK из нескольких колонок. «Два PK» — неверный термин.

Ошибка 5. NULL в FK — путаница

FK может быть NULL, если столбец допускает. Это означает «нет связи», не «сломанная связь».

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

FAQ

Может ли быть таблица без PK?

Технически да (для staging / временных). Но в нормальной БД — всегда PK.

Может ли FK быть NULL?

Да, если столбец NULLable. Означает «связи нет».

Сколько FK может быть в таблице?

Неограниченно (в разумных пределах). Junction tables часто имеют 2-3 FK.

PK всегда integer?

Нет. Может быть UUID, string, composite. Integer auto-increment — популярный выбор, но не единственный.


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