Связи один-к-одному, один-ко-многим, многие-ко-многим

Почему связи между таблицами — это важно? 🔗

Представьте библиотеку: книги стоят на полках в случайном порядке без какой-либо системы. Найти нужную будет почти невозможно! Так же и с базами данных: связи между таблицами — это "система хранения", которая делает данные удобными для работы.

В SQL есть три основных типа связей:

  1. Один-к-одному (1:1)
  2. Один-ко-многим (1:M)
  3. Многие-ко-многим (M:M)

Разберём каждую на реальных примерах с кодом!


Связь один-к-одному (1:1) 👥➡👤

Суть: Одна запись в таблице А соответствует ровно одной записи в таблице Б.

Где применяется? Когда нужно разделить данные по разным таблицам из-за: - Безопасности (например, данные паспорта отдельно от основной информации) - Оптимизации (редко используемые данные выносятся в отдельную таблицу)

Пример: Таблица пользователей и их паспортных данных.

-- Основная информация
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL
);

-- Паспортные данные (отдельно для безопасности)
CREATE TABLE passports (
    passport_id INT PRIMARY KEY,
    user_id INT UNIQUE NOT NULL,
    passport_number VARCHAR(20) NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(user_id)
);

Важно: Обратите внимание на UNIQUE для user_id в таблице passports — это гарантирует связь 1:1.


Связь один-ко-многим (1:M) 👤➡👥👥👥

Суть: Одна запись в таблице А может быть связана с несколькими записями в таблице Б.

Где применяется? Самый распространённый тип связи! Например: - Один автор → много книг - Одна страна → много городов - Один заказ → много товаров в заказе

Пример: Блог с постами и комментариями.

CREATE TABLE posts (
    post_id INT PRIMARY KEY,
    title VARCHAR(100) NOT NULL,
    content TEXT NOT NULL
);

CREATE TABLE comments (
    comment_id INT PRIMARY KEY,
    post_id INT NOT NULL,
    author VARCHAR(50) NOT NULL,
    text TEXT NOT NULL,
    FOREIGN KEY (post_id) REFERENCES posts(post_id)
    ON DELETE CASCADE  -- Удалится пост → удалятся комментарии
);

Практика: Вставьте несколько комментариев для одного поста — это наглядная демонстрация 1:M!


Связь многие-ко-многим (M:M) 👥👥👥⇄👥👥👥

Суть: Множество записей в таблице А связано с множеством записей в таблице Б.

Где применяется? Когда объекты имеют сложные взаимосвязи: - Студенты и курсы (один студент на многих курсах, один курс у многих студентов) - Теги и статьи - Актеры и фильмы

Как реализовать? Через промежуточную таблицу (junction table)!

Пример: Система обучения с курсами и студентами.

CREATE TABLE students (
    student_id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

CREATE TABLE courses (
    course_id INT PRIMARY KEY,
    title VARCHAR(100) NOT NULL
);

-- Промежуточная таблица
CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrollment_date DATE NOT NULL,
    PRIMARY KEY (student_id, course_id),  -- Составной первичный ключ
    FOREIGN KEY (student_id) REFERENCES students(student_id),
    FOREIGN KEY (course_id) REFERENCES courses(course_id)
);

Совет: Составной первичный ключ (PRIMARY KEY (student_id, course_id)) предотвращает дублирование связей.


Визуализируем связи 🎨

Лучший способ понять разницу — представить схемы:

  1. 1:1 — две таблицы соединяет одна линия (как мост между двумя островами)
  2. 1:M — от одной таблицы расходятся несколько линий (как дерево с ветвями)
  3. M:M — две таблицы связаны через третью (капля воды в центре, от которой идут лучи к обеим таблицам)

Практическое задание 💻

Создайте базу данных для интернет-магазина: 1. Таблица products (товары) 2. Таблица categories (категории) 3. Реализуйте связь M:M между ними (товар может быть в нескольких категориях, категория содержит много товаров)

Подсказка: Вам понадобится промежуточная таблица product_categories.

-- Ваше решение здесь!

Главные ошибки новичков ❌

  1. Путаница между 1:M и M:M — запомните: если для связи нужна третья таблица, это M:M!
  2. Отсутствие ограничений внешнего ключа — без FOREIGN KEY база не будет контролировать целостность данных.
  3. Игнорирование каскадных операций — решите, что должно происходить при удалении связанных данных (ON DELETE CASCADE/SET NULL).

Продвинутый уровень: индексы для связей 🚀

Для ускорения поиска по связанным полям добавляйте индексы:

-- Для связи 1:M в таблице comments
CREATE INDEX idx_comments_post_id ON comments(post_id);

-- Для связи M:M в промежуточной таблице
CREATE INDEX idx_sc_student_id ON student_courses(student_id);
CREATE INDEX idx_sc_course_id ON student_courses(course_id);

Теперь JOIN-запросы будут работать значительно быстрее!


Итоги 🏁

1:1 — редкий случай, используйте для разделения данных
1:M — самая частая связь, реализуется через внешний ключ
M:M — требует промежуточной таблицы с двумя внешними ключами

При проектировании всегда задавайте вопросы:
"Сколько объектов А может быть связано с объектом Б?" — и схема станет очевидной!

Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty