Foreign Key (внешний ключ) — это столбец или набор столбцов, который создаёт связь между двумя таблицами, ссылаясь на PRIMARY KEY другой таблицы. Главная цель — обеспечить ссылочную целостность данных.
Ключевые аспекты
Ссылочная целостность — FK гарантирует, что значение существует в родительской таблице
Связь таблиц — FK определяет отношение между родительской и дочерней таблицей
Каскадные операции — автоматические действия при удалении/обновлении родительской записи
Индексация — СУБД обычно создаёт индекс для FK, ускоряя JOIN
Зачем нужен Foreign Key?
Предотвращает создание «сиротских» записей (ссылки на несуществующие данные)
Автоматизирует контроль целостности на уровне БД
Документирует связи между таблицами в схеме
Позволяет использовать каскадное удаление/обновление
Плюсы
Надёжная защита целостности данных
Не зависит от логики приложения
Упрощает понимание структуры БД
Минусы
Замедляет операции INSERT/UPDATE/DELETE (дополнительные проверки)
Усложняет миграции и массовые операции
Требует правильного порядка вставки/удаления данных
Частые ошибки на собеседованиях
Путают PRIMARY KEY и FOREIGN KEY
Не знают о каскадных операциях (ON DELETE CASCADE)
Забывают, что FK может содержать NULL (если не указан NOT NULL)
Введение и проблематика
Представьте таблицу заказов, где user_id ссылается на пользователя. Без Foreign Key ничто не мешает записать user_id = 9999, даже если такого пользователя не существует. Это создаёт «сиротские» записи — данные, ссылающиеся в никуда.
sql
-- ❌ Без FOREIGN KEY: база примет любое значениеINSERT INTO orders (user_id, total) VALUES (9999, 100.00);-- Успешно! Но пользователя 9999 не существует...
Foreign Key решает эту проблему, запрещая вставку значений, которые не существуют в родительской таблице.
Базовая теория
Что такое Foreign Key?
Foreign Key (FK, внешний ключ) — это столбец (или группа столбцов), который ссылается на PRIMARY KEY другой таблицы. FK создаёт связь между двумя таблицами и обеспечивает ссылочную целостность (referential integrity).
Терминология
Термин
Описание
Родительская таблица
Таблица с PRIMARY KEY, на которую ссылаются
Дочерняя таблица
Таблица с FOREIGN KEY, которая ссылается
Ссылочная целостность
Гарантия существования связанной записи
Каскадные операции
Автоматические действия при изменении родителя
Визуализация связи
erDiagram USERS ||--o{ ORDERS :"has" USERS {int id PKvarchar namevarchar email} ORDERS {int id PKint user_id FKdecimal totaltimestamp created_at}
Практические примеры
Создание FOREIGN KEY при создании таблицы
sql
-- Родительская таблицаCREATETABLEusers ( id SERIALPRIMARY KEY,nameVARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUENOT NULL);-- Дочерняя таблица с FKCREATETABLEorders ( id SERIALPRIMARY KEY, user_id INTNOT NULL, total DECIMAL(10, 2) NOT NULL, created_at TIMESTAMPDEFAULT CURRENT_TIMESTAMP,CONSTRAINT fk_orders_userFOREIGN KEY (user_id) REFERENCES users(id));
Проверка ссылочной целостности
sql
-- Вставляем пользователяINSERT INTO users (name, email) VALUES ('John', 'john@mail.com');-- id = 1-- ✅ Успешно: пользователь существуетINSERT INTO orders (user_id, total) VALUES (1, 99.99);-- ❌ Ошибка: пользователя с id=999 не существуетINSERT INTO orders (user_id, total) VALUES (999, 50.00);-- ERROR: insert or update on table "orders" violates foreign key constraint
Каскадные операции
Каскадные операции определяют, что происходит с дочерними записями при удалении или обновлении родительской.
ON DELETE — при удалении родителя
sql
CREATETABLEorders ( id SERIALPRIMARY KEY, user_id INT, total DECIMAL(10, 2),FOREIGN KEY (user_id) REFERENCES users(id)ON DELETE CASCADE-- удалить заказы при удалении пользователя);
Варианты поведения
Опция
Описание
Когда использовать
CASCADE
Удалить/обновить связанные записи
Зависимые данные (заказы пользователя)
SET NULL
Установить NULL
Опциональная связь (автор удалён)
SET DEFAULT
Установить значение по умолчанию
Резервное значение
RESTRICT
Запретить операцию
Защита от случайного удаления
NO ACTION
То же, что RESTRICT
По умолчанию
Примеры использования
sql
-- При удалении пользователя удаляются все его заказыFOREIGN KEY (user_id) REFERENCES users(id)ON DELETE CASCADEONUPDATE CASCADE;-- ПримерDELETEFROM users WHERE id =1;-- Автоматически удалит все заказы с user_id = 1
Пограничные кейсы
FK может быть NULL
⚠️
FOREIGN KEY не подразумевает NOT NULL. Если связь опциональная, FK может быть NULL.
sql
CREATETABLEarticles ( id SERIALPRIMARY KEY, title VARCHAR(255) NOT NULL, author_id INT, -- может быть NULL (анонимная статья)FOREIGN KEY (author_id) REFERENCES users(id));-- ✅ Успешно: статья без автораINSERT INTO articles (title, author_id) VALUES ('Anonymous Post', NULL);
Составной FOREIGN KEY
sql
-- FK может ссылаться на составной PRIMARY KEYCREATETABLEorder_items ( order_id INT, product_id INT, warehouse_id INT, quantity INT,PRIMARY KEY (order_id, product_id),FOREIGN KEY (warehouse_id, product_id)REFERENCES inventory(warehouse_id, product_id));
Самоссылающийся FK (Self-referencing)
sql
-- Иерархия сотрудников: каждый может иметь руководителяCREATETABLEemployees ( id SERIALPRIMARY KEY,nameVARCHAR(100) NOT NULL, manager_id INT,FOREIGN KEY (manager_id) REFERENCES employees(id));-- CEO без руководителяINSERT INTO employees (name, manager_id) VALUES ('CEO', NULL);-- Менеджер подчиняется CEOINSERT INTO employees (name, manager_id) VALUES ('Manager', 1);
Порядок операций
🚫
Порядок важен! При INSERT сначала родитель, потом дочерний. При DELETE — наоборот (или используйте CASCADE).
Правильная вставка
sql
-- 1. Сначала родительINSERT INTO users (name, email) VALUES ('Alice', 'alice@mail.com');-- 2. Потом дочерний (FK уже существует)INSERT INTO orders (user_id, total) VALUES (1, 150.00);
Правильное удаление (без CASCADE)
sql
-- 1. Сначала дочерние записиDELETEFROM orders WHERE user_id =1;-- 2. Потом родительDELETEFROM users WHERE id =1;
Производительность
Аспект
Влияние
SELECT/JOIN
✅ FK создаёт индекс → быстрые JOIN
INSERT
❌ Проверка существования родителя
UPDATE
❌ Проверка при изменении FK
DELETE
❌ Проверка дочерних записей
Несмотря на небольшое замедление записи, FK критически важен для целостности данных. Это trade-off, который почти всегда оправдан.
Сравнение: PRIMARY KEY vs FOREIGN KEY
Аспект
PRIMARY KEY
FOREIGN KEY
Назначение
Идентификация записи
Связь с другой таблицей
Уникальность
Обязательна
Не требуется
NULL
Запрещён
Допустим (если нет NOT NULL)
Количество в таблице
Один
Много
Индекс
Создаётся автоматически
Создаётся автоматически (в большинстве СУБД)
Вопросы интервьюера
Q: Зачем нужен FOREIGN KEY, если можно делать JOIN без него?
FK обеспечивает ссылочную целостность — гарантию, что связанные данные существуют. Без FK возможны «сиротские» записи.
Q: Что такое ссылочная целостность?
Это гарантия, что значение FOREIGN KEY всегда указывает на существующую запись в родительской таблице.
Q: Когда использовать ON DELETE CASCADE?
Когда дочерние записи не имеют смысла без родителя. Например, элементы корзины при удалении корзины.
Q: Влияет ли FK на производительность?
Да, немного замедляет INSERT/UPDATE/DELETE из-за проверок. Но JOIN ускоряется благодаря автоматическому индексу.
Q: Может ли FOREIGN KEY ссылаться на не PRIMARY KEY?
Да, может ссылаться на любой столбец с UNIQUE constraint.