Relationship (связь) между таблицами — это логическая связь, которая соединяет данные из разных таблиц на основе общих значений. В SQL связи реализуются через FOREIGN KEY, который ссылается на PRIMARY KEY другой таблицы.
Ключевые аспекты
PRIMARY KEY — уникальный идентификатор записи в родительской таблице
FOREIGN KEY — столбец в дочерней таблице, хранящий ссылку на родительскую
Ссылочная целостность — гарантия, что FOREIGN KEY указывает на существующую запись
Нормализация — разделение данных по таблицам для устранения дублирования
Зачем нужны связи?
Устранение дублирования данных
Поддержание консистентности информации
Логическая организация данных
Возможность делать JOIN-запросы
Плюсы
Экономия места (данные не дублируются)
Изменение данных в одном месте обновляет всю систему
Структурированность и понятность схемы
Минусы
Требуется понимание нормализации
JOIN-запросы сложнее простых SELECT
Производительность может снижаться при многих JOIN
Частые ошибки на собеседованиях
Путают связь (relationship) с JOIN-операцией
Не понимают разницу между логической связью и физическим FOREIGN KEY
Забывают про каскадные операции при удалении связанных записей
Введение и проблематика
Представьте интернет-магазин, где нужно хранить информацию о пользователях и их заказах. Можно записать всё в одну таблицу:
sql
-- ❌ Плохой подход: всё в одной таблице| order_id | user_name | user_email | product | price ||----------|-----------|-----------------|----------|-------|| 1 | John | john@mail.com | Laptop | 999 || 2 | John | john@mail.com | Mouse | 25 || 3 | John | john@mail.com | Keyboard | 75 |
Проблемы очевидны:
Дублирование: имя и email Джона повторяются 3 раза
Аномалии обновления: изменение email требует обновления всех строк
Аномалии удаления: удаление последнего заказа удалит и данные пользователя
Связи между таблицами (relationships) решают эти проблемы, разделяя данные по логическим сущностям и соединяя их через ключи.
Базовая теория
Что такое relationship?
Relationship (связь) — это логическое соединение между двумя таблицами, основанное на общих данных. Одна таблица содержит PRIMARY KEY, другая — FOREIGN KEY, который ссылается на этот PRIMARY KEY.
Терминология
Термин
Описание
Родительская таблица
Таблица с PRIMARY KEY (на которую ссылаются)
Дочерняя таблица
Таблица с FOREIGN KEY (которая ссылается)
Ссылочная целостность
Гарантия, что FOREIGN KEY указывает на существующую запись
Кардинальность
Тип связи: один-к-одному, один-ко-многим, многие-ко-многим
Практические примеры
Разделение данных по таблицам
sql
-- ✅ Правильный подход: две связанные таблицы-- Родительская таблицаCREATETABLEusers ( id SERIALPRIMARY KEY,nameVARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUENOT NULL);-- Дочерняя таблица со связьюCREATETABLEorders ( id SERIALPRIMARY KEY, user_id INTNOT NULL, product VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL,FOREIGN KEY (user_id) REFERENCES users(id));
-- Получить все заказы с информацией о пользователеSELECT u.name, u.email, o.product, o.priceFROM orders oJOIN users u ON o.user_id = u.id;
Результат:
text
| name | email | product | price ||------|---------------|----------|-------|| John | john@mail.com | Laptop | 999 || John | john@mail.com | Mouse | 25 || John | john@mail.com | Keyboard | 75 |
Визуализация связи
erDiagram USERS ||--o{ ORDERS :"has many" USERS {int id PKvarchar namevarchar email} ORDERS {int id PKint user_id FKvarchar productdecimal price}
Нотация:
|| — один (обязательный)
o{ — много (опционально)
PK — Primary Key
FK — Foreign Key
Как работает FOREIGN KEY
FOREIGN KEY обеспечивает ссылочную целостность — невозможно создать заказ для несуществующего пользователя.
sql
-- ✅ Успешно: пользователь с id=1 существуетINSERT INTO orders (user_id, product, price) VALUES (1, 'Phone', 599);-- ❌ Ошибка: пользователь с id=999 не существуетINSERT INTO orders (user_id, product, price) VALUES (999, 'Tablet', 399);-- Error: a foreign key constraint fails
Каскадные операции
Что делать при удалении пользователя с заказами?
sql
CREATETABLEorders ( id SERIALPRIMARY KEY, user_id INT, product VARCHAR(255),FOREIGN KEY (user_id) REFERENCES users(id)ON DELETE CASCADE-- удалить все заказы пользователяONUPDATE CASCADE -- обновить user_id при изменении);
Варианты поведения:
Действие
Описание
CASCADE
Автоматически удалить/обновить связанные записи
SET NULL
Установить NULL в дочерних записях
SET DEFAULT
Установить значение по умолчанию
RESTRICT
Запретить операцию, если есть связанные записи
NO ACTION
То же, что RESTRICT (по умолчанию)
Пограничные кейсы
⚠️
Порядок операций важен! При вставке: сначала родительская запись, потом дочерняя. При удалении: сначала дочерние, потом родительская (или используйте CASCADE).
Правильный порядок вставки
sql
-- 1. Сначала создаём пользователяINSERT INTO users (name, email) VALUES ('Alice', 'alice@mail.com');-- Получаем id = 2-- 2. Потом создаём заказINSERT INTO orders (user_id, product, price) VALUES (2, 'Monitor', 299);
Правильный порядок удаления (без CASCADE)
sql
-- 1. Сначала удаляем заказы пользователяDELETEFROM orders WHERE user_id =2;-- 2. Потом удаляем пользователяDELETEFROM users WHERE id =2;
Связь vs JOIN
Relationship — это структурная связь между таблицами:
Определяется при создании таблицы
Реализуется через FOREIGN KEY
Обеспечивает целостность данных
Существует независимо от запросов
sql
-- Определение связиFOREIGN KEY (user_id) REFERENCES users(id)
Можно делать JOIN без FOREIGN KEY, но тогда база данных не защитит от «сиротских» записей.
Преимущества связей между таблицами
Аспект
Без связей
Со связями
Дублирование
❌ Данные повторяются
✅ Каждый факт хранится один раз
Обновление
❌ Нужно менять много строк
✅ Одно изменение в одном месте
Целостность
❌ Возможны inconsistent данные
✅ FOREIGN KEY гарантирует связь
Размер БД
❌ Больше места на диске
✅ Меньше места
Запросы
✅ Простой SELECT
❌ Нужен JOIN
Вопросы интервьюера
Q: Чем relationship отличается от JOIN?
Relationship — это структурная связь через FOREIGN KEY. JOIN — операция в SQL-запросе для объединения данных. Можно делать JOIN без relationship, но тогда нет гарантии целостности.
Q: Можно ли сделать связь между таблицами без FOREIGN KEY?
Технически да, используя обычный столбец и JOIN по значениям. Но без FOREIGN KEY база не защитит от «сиротских» записей.
Q: Что такое ссылочная целостность?
Гарантия того, что значение FOREIGN KEY всегда указывает на существующую запись в родительской таблице.
Q: Как удалить запись, на которую есть ссылки?
Варианты: сначала удалить дочерние записи, использовать ON DELETE CASCADE, или ON DELETE SET NULL.