Что такое foreign key в PostgreSQL?
Что такое Foreign Key
Foreign Key (внешний ключ) — это ограничение целостности (CONSTRAINT), которое создаёт связь между двумя таблицами. Столбец или группа столбцов в дочерней таблице должны содержать только те значения, которые уже существуют в указанном столбце родительской (referenced) таблицы.
Простыми словами: если у вас есть таблица orders со столбцом user_id, foreign key гарантирует, что в user_id нельзя записать идентификатор несуществующего пользователя.
Синтаксис объявления
При создании таблицы
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
amount NUMERIC(10, 2),
-- объявляем внешний ключ
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)
Когда запись в родительской таблице удаляется или обновляется, PostgreSQL может вести себя по-разному — это настраивается через ON DELETE и ON UPDATE:
| Действие | Поведение |
|---|---|
NO ACTION | Ошибка, если есть зависимые строки (по умолчанию) |
RESTRICT | Аналогично NO ACTION, но проверка немедленная |
CASCADE | Удалить / обновить зависимые строки автоматически |
SET NULL | Установить NULL в зависимом столбце |
SET DEFAULT | Установить значение по умолчанию |
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER,
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE SET NULL -- при удалении пользователя обнуляем user_id
ON UPDATE CASCADE -- при смене id пользователя обновляем user_id
);
Составной внешний ключ
Foreign key может ссылаться на несколько столбцов одновременно:
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
-- составной внешний ключ
CONSTRAINT fk_order_items
FOREIGN KEY (order_id, product_id)
REFERENCES order_products(order_id, product_id)
);
Важные нюансы
- Столбец, на который ссылается foreign key, должен иметь ограничение
PRIMARY KEYилиUNIQUE. - По умолчанию PostgreSQL создаёт индекс на первичном ключе, но не создаёт автоматически индекс на столбце внешнего ключа в дочерней таблице — его нужно добавлять вручную для ускорения JOIN-запросов и каскадных операций.
- Foreign key можно временно отключить командой
SET session_replication_role = replica;(используется при массовой загрузке данных).
Что хочет услышать интервьюер
Понимание, что foreign key обеспечивает ссылочную целостность между таблицами
Знание синтаксиса: FOREIGN KEY ... REFERENCES ...
Понимание опций ON DELETE / ON UPDATE (особенно CASCADE и SET NULL)
Осознание, что referenced-столбец должен быть PRIMARY KEY или UNIQUE
Знание о том, что индекс на FK-столбце не создаётся автоматически и его нужно добавлять вручную
Пример: Базовый пример: таблицы users и orders
-- Родительская таблица
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
-- Дочерняя таблица с внешним ключом
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
amount NUMERIC(10, 2),
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE CASCADE
);
-- Добавляем индекс на FK-столбец вручную
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- Вставка корректной записи
INSERT INTO users (name) VALUES ('Иван');
INSERT INTO orders (user_id, amount) VALUES (1, 1500.00);
-- Попытка вставить запись с несуществующим user_id — ошибка
INSERT INTO orders (user_id, amount) VALUES (999, 500.00);
-- ERROR: insert or update on table "orders" violates foreign key constraint
Типичные ошибки
Путают направление связи: думают, что FK можно ставить на любой столбец, а не только ссылающийся на PK/UNIQUE
Не знают разницу между ON DELETE RESTRICT и ON DELETE NO ACTION (RESTRICT проверяет немедленно, NO ACTION — в конце транзакции)
Забывают создавать индекс на столбце внешнего ключа, что приводит к медленным JOIN и каскадным удалениям
Считают, что CASCADE удаляет только один уровень вложенности, а не рекурсивно все зависимые записи
Не понимают, что NULL-значение в FK-столбце допустимо и не нарушает ограничение (NULL не ссылается ни на что)


