Ключ в базе данных — это столбец или набор столбцов таблицы, значения которых позволяют однозначно найти нужную строку или связать строки разных таблиц. Ключи решают две задачи: уникальную идентификацию записи и целостность связей между таблицами.
Содержание
Ниже — виды ключей в БД (первичный, внешний, уникальный, составной, потенциальный, суррогатный, естественный), рабочие примеры CREATE TABLE на PostgreSQL, типовые ошибки СУБД и сводные таблицы. Все примеры собраны на одной схеме из трёх таблиц.
Виды ключей в БД
Слово «ключ» в реляционной модели используют в нескольких смыслах, и полезно развести два уровня: логическую модель (про смысл данных) и SQL-ограничения (которыми её реализуют в таблице).
Логическая модель — это потенциальный ключ (минимальный набор столбцов, уникально определяющий строку; их может быть несколько), естественный (из реальных данных: ИНН, паспорт, код товара) и суррогатный (искусственный идентификатор без смысла, автоинкремент или UUID).
На SQL-уровне модель реализуют ограничения PRIMARY KEY, UNIQUE, FOREIGN KEY и составной ключ (сводка ниже).
Важная оговорка про UNIQUE: это ограничение уникальности, и потенциальный ключ реализует только минимальный набор UNIQUE NOT NULL. Nullable UNIQUE в PostgreSQL по умолчанию допускает несколько NULL, поэтому не всякое UNIQUE — это потенциальный ключ.
Зачем нужны ключи: идентификация и целостность
Первая задача — идентификация: без первичного ключа строки можно перепутать (два заказа с одной суммой), а с ним каждая строка адресуема.
Вторая задача — целостность связей: внешний ключ не даёт сослаться на несуществующего клиента и не даёт удалить клиента с заказами, пока вы не укажете, что делать с зависимыми строками. По умолчанию PostgreSQL такое удаление запрещает (NO ACTION); варианты разберём ниже.
Про индексы важна привязка к диалекту. В PostgreSQL под PRIMARY KEY и UNIQUE СУБД создаёт уникальный индекс, поэтому поиск и соединение по ним быстры. А вот FOREIGN KEY индекс на ссылающихся столбцах не создаёт автоматически — при необходимости его создают отдельно через CREATE INDEX.
Примеры на SQL
Основной диалект дальше — PostgreSQL; для MySQL 8 отличается генерация ключа, она вынесена отдельной врезкой, а не смешана в одном блоке.
Простой первичный ключ задают в CREATE TABLE; значение генерирует столбец-идентификатор:
-- PostgreSQL: значение supplier_id генерирует сама СУБД
CREATE TABLE suppliers (
supplier_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- суррогатный первичный ключ
inn VARCHAR(12) UNIQUE, -- естественный уникальный ключ: дубли ИНН запрещены
name VARCHAR(200) NOT NULL
);
Здесь supplier_id — суррогатный первичный ключ, inn — уникальный. Вариант GENERATED ALWAYS запрещает подставлять значение вручную, а GENERATED BY DEFAULT AS IDENTITY — разрешает (его используем ниже для goods, чтобы воспроизвести ошибки).
В MySQL 8 генерацию задаёт AUTO_INCREMENT:
-- MySQL 8: та же таблица, значение задаёт AUTO_INCREMENT
CREATE TABLE suppliers (
supplier_id INT AUTO_INCREMENT PRIMARY KEY,
inn VARCHAR(12) UNIQUE,
name VARCHAR(200) NOT NULL
);
Рабочая схема: связь «многие ко многим»
Связь «многие ко многим» требует нескольких таблиц. Соберём единый запускаемый блок: справочник товаров goods, заказы orders и позиции order_items.
-- PostgreSQL: справочник товаров
CREATE TABLE goods (
good_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- суррогатный первичный ключ
name VARCHAR(200) NOT NULL,
type VARCHAR(50) NOT NULL
);
-- заказы
CREATE TABLE orders (
order_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
created_at DATE NOT NULL
);
-- позиции заказа: таблица-связка «многие ко многим»
CREATE TABLE order_items (
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id), -- составной первичный ключ
FOREIGN KEY (order_id) REFERENCES orders(order_id), -- позиция ссылается на заказ
FOREIGN KEY (product_id) REFERENCES goods(good_id) -- и на товар из справочника
);
Это типовая связка «многие ко многим»: заказ содержит много товаров, товар встречается во многих заказах, а пара «заказ + товар» уникальна за счёт составного ключа. Два внешних ключа держат целостность: order_id обязан существовать в orders, а product_id — в goods.
Наполним схему — три INSERT заводят товары, заказ и состав:
INSERT INTO goods (name, type) VALUES ('Стол', 'equipment'), ('Стул', 'equipment');
INSERT INTO orders (created_at) VALUES ('2026-09-07');
INSERT INTO order_items (order_id, product_id, quantity) VALUES (1, 1, 4), (1, 2, 8);
Идентификаторы сгенерировались сами: у товаров good_id = 1 и 2, у заказа order_id = 1. Позиции ссылаются на существующие строки — вставка проходит.
Ошибки с ключами: нарушение, сообщение СУБД, исправление
Ключи проявляются в момент нарушения. Три частые ошибки на схеме goods/orders/order_items (сообщения PostgreSQL).
Ошибка 1 — дубликат по первичному ключу.
INSERT INTO goods (good_id, name, type) VALUES (1, 'Лампа', 'equipment'); -- good_id=1 уже занят
ERROR: duplicate key value violates unique constraint "goods_pkey"
DETAIL: Key (good_id)=(1) already exists.
Исправление: не задавать ключ вручную — INSERT INTO goods (name, type) VALUES ('Лампа', 'equipment');.
Ошибка 2 — NULL в первичном ключе.
INSERT INTO goods (good_id, name, type) VALUES (NULL, 'Стол', 'equipment');
ERROR: null value in column "good_id" violates not-null constraint
Исправление: опустить столбец или указать DEFAULT и дать ключ сгенерировать; NULL в первичный ключ вставлять нельзя.
Ошибка 3 — нарушение внешнего ключа.
INSERT INTO order_items (order_id, product_id, quantity) VALUES (1, 999, 2); -- товара 999 нет
ERROR: insert or update on table "order_items" violates foreign key constraint
DETAIL: Key (product_id)=(999) is not present in table "goods".
Исправление: сначала завести товар в goods, потом ссылаться на его good_id, либо указать существующий.
Частый учебный приём COUNT(*) + 1 для good_id в разовом примере проходит, но ненадёжен: при удалении строк или одновременной вставке два товара получат одинаковый good_id и сработает ошибка 1. Генерацию отдают СУБД (GENERATED ... AS IDENTITY или AUTO_INCREMENT).
FOREIGN KEY и удаление родителя: ON DELETE
Что происходит при удалении строки, на которую ссылается внешний ключ, задаёт ON DELETE. По умолчанию PostgreSQL использует NO ACTION. Три частых варианта:
| ON DELETE | Что делает при удалении родительской строки | Когда выбирать |
|---|---|---|
| RESTRICT / NO ACTION | Запрещает удаление, пока есть зависимые строки | Осиротевшие данные недопустимы |
| CASCADE | Удаляет зависимые строки вместе с родителем | Дочерние строки без родителя бессмысленны (позиции удаляемого заказа) |
| SET NULL | Обнуляет внешний ключ в зависимых строках | Связь необязательна (столбец должен допускать NULL) |
Вариант задают при объявлении: FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE.
Успешный запрос: SELECT с JOIN
Соединение по ключам собирает читаемый результат:
SELECT o.order_id, g.name AS good, oi.quantity
FROM order_items oi
JOIN orders o ON o.order_id = oi.order_id
JOIN goods g ON g.good_id = oi.product_id
ORDER BY o.order_id, g.name;
order_id | good | quantity
----------+------+----------
1 | Стол | 4
1 | Стул | 8
Внешние ключи гарантируют, что в order_items нет позиций без заказа или товара, а составной ключ — что пара не задвоится.
Виды ключей: сводные таблицы
Логическая модель — про смысл данных:
| Понятие | Что значит | Пример |
|---|---|---|
| Потенциальный (candidate) | Минимальный уникальный набор столбцов | Пара (order_id, product_id) |
| Естественный | Ключ из реальных данных | ИНН, паспорт, код товара |
| Суррогатный | Искусственный идентификатор без смысла | good_id из IDENTITY |
SQL-ограничения — чем это реализуют в таблице:
| Ограничение | Назначение | Пример объявления |
|---|---|---|
| PRIMARY KEY | Главный идентификатор строки, без NULL, один на таблицу | good_id INT ... PRIMARY KEY |
| UNIQUE | Запрет дублей; UNIQUE NOT NULL может задавать потенциальный ключ | inn VARCHAR(12) UNIQUE |
| FOREIGN KEY | Ссылка на ключ другой таблицы, целостность связей | FOREIGN KEY (product_id) REFERENCES goods(good_id) |
| Составной (composite) | Ключ из двух и более столбцов; уникальна комбинация | PRIMARY KEY (order_id, product_id) |
Выводы
- Разводите два уровня: логическая модель (потенциальный, естественный, суррогатный) и SQL-ограничения (PRIMARY KEY, UNIQUE, FOREIGN KEY), которыми её реализуют.
- Первичный ключ один, без NULL; UNIQUE может быть несколько, а nullable UNIQUE в PostgreSQL допускает несколько NULL, поэтому потенциальный ключ задаёт только минимальный UNIQUE NOT NULL.
- Внешний ключ держит целостность; поведение при удалении родителя задаёт ON DELETE (NO ACTION/RESTRICT, CASCADE, SET NULL).
- В PostgreSQL PRIMARY KEY и UNIQUE создают уникальный индекс, а FOREIGN KEY — нет; индекс на его столбцах при необходимости создают отдельно. Генерацию ключа отдавайте СУБД, а не считайте записи вручную.
Где применяется / связь с практикой
Освойте тему на практике
Ключи — фундамент любой схемы данных, с ними ежедневно работают бэкенд-разработчики, аналитики и администраторы БД. Разобрать проектирование схем, нормализацию и работу с ключами помогает курс по базам данных subd. Если хочется сначала присмотреться к теме, посмотрите бесплатные вебинары — там разбирают темы по базам данных и SQL вживую.
Смежные темы: Введение в SQL: что требуется знать новичку, Изучение SQL-команд на базе MySQL, Знакомство с SQLite: что нужно знать о СУБД.
FAQ
Чем потенциальный ключ отличается от первичного? Потенциальный (candidate) — любой минимальный набор столбцов, уникально определяющий строку; их бывает несколько. Первичный — выбранный из них главным.
Может ли внешний ключ ссылаться не на первичный ключ? Да, на первичный или уникальный (UNIQUE) ключ другой таблицы — важно, чтобы целевой столбец гарантировал уникальность.
Любое ли UNIQUE — это потенциальный ключ? Нет. Nullable UNIQUE в PostgreSQL по умолчанию разрешает несколько строк без значения, поэтому потенциальный ключ задаёт только минимальный набор UNIQUE NOT NULL.



