JavaRush /Курсы /SQL SELF /Моделирование связи MANY-TO-MANY с помощью промежуточной ...

Моделирование связи MANY-TO-MANY с помощью промежуточной таблицы

SQL SELF
19 уровень , 4 лекция
Открыта

Связь "многие-ко-многим" — это когда одна запись в одной таблице может быть связана с несколькими записями в другой таблице, и наоборот. Например: - Один студент (из таблицы students) может быть записан на несколько курсов (из таблицы courses). - Один курс может быть посещён несколькими студентами.

Проблема в том, что напрямую такую связь хранить неудобно. Поэтому на помощь приходит промежуточная таблица, которая хранит эти связи.

Пример из реальной жизни

Представьте, что вы завели таблицу студентов и таблицу курсов. Если вы попытаетесь добавить в каждую таблицу колонку для хранения всех связанных данных, у вас будет хаос:

  • В таблице students придётся хранить список всех курсов, на которые записан студент. Но как хранить список? Через запятую? В массиве? Это будет сложно для обработки запросов.
  • В таблице courses придётся хранить список студентов, а это вообще вызывает головную боль.

Поэтому правильное решение — создать третью таблицу, которая будет хранить связи между студентами и курсами.

Промежуточная таблица: наше спасение!

Промежуточная таблица (иногда её называют таблицей связей) решает все проблемы. Она содержит две внешние ссылки:

  • Внешний ключ на таблицу students.
  • Внешний ключ на таблицу courses.

Каждая запись в этой таблице создаёт связь между конкретным студентом и конкретным курсом.

Создание таблиц для связи "многие-ко-многим"

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

Шаг 1: Создание таблицы students

Вот наша таблица для студентов. Здесь мы храним уникальные идентификаторы студентов и их имена.

CREATE TABLE students (
    student_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);
  • student_id — это уникальный идентификатор студента (автоинкрементируемый, спасибо SERIAL!).
  • name — имя студента.

Шаг 2: Создание таблицы courses

Теперь создадим таблицу для курсов. Здесь мы храним уникальные идентификаторы курсов и их названия.

CREATE TABLE courses (
    course_id SERIAL PRIMARY KEY,
    title TEXT NOT NULL
);
  • course_id — уникальный идентификатор курса.
  • title — название курса.

Шаг 3: Создание промежуточной таблицы enrollments

Теперь создадим нашу волшебную таблицу-связку. Она содержит две колонки, каждая из которых является внешним ключом, указывающим на соответствующие таблицы.

CREATE TABLE enrollments (
    student_id INT REFERENCES students(student_id),
    course_id INT REFERENCES courses(course_id),
    PRIMARY KEY (student_id, course_id)
);

Разберём структуру:

  • student_id — внешний ключ, ссылающийся на student_id из таблицы students.
  • course_id — внешний ключ, ссылающийся на course_id из таблицы courses.
  • PRIMARY KEY (student_id, course_id) — первичный ключ состоит из комбинации двух внешних ключей. Это гарантирует, что каждая связь будет уникальной.

Вставка данных

Давайте добавим немного данных, чтобы проверить, как всё работает.

Шаг 1: Добавление студентов

INSERT INTO students (name) VALUES
('Alice'),
('Bob'),
('Charlie');

Результат:

student_id name
1 Alice
2 Bob
3 Charlie

Шаг 2: Добавление курсов

INSERT INTO courses (title) VALUES
('Mathematics'),
('History'),
('Biology');

Результат:

course_id title
1 Mathematics
2 History
3 Biology

Шаг 3: Добавление записей в enrollments

Теперь запишем студентов на курсы. Например:

  • Alice записана на Mathematics и History.
  • Bob записан только на Biology.
  • Charlie записан на все три курса.
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 1), -- Alice на Mathematics
(1, 2), -- Alice на History
(2, 3), -- Bob на Biology
(3, 1), -- Charlie на Mathematics
(3, 2), -- Charlie на History
(3, 3); -- Charlie на Biology

Результат:

student_id course_id
1 1
1 2
2 3
3 1
3 2
3 3

Запросы для связи "многие-ко-многим"

Теперь, когда у нас есть данные, пора получить от них пользу!

Как найти все курсы, на которые записан студент?

Например, чтобы узнать, на какие курсы записан Alice (ID = 1), выполните запрос:

SELECT c.title
FROM courses c
JOIN enrollments e ON c.course_id = e.course_id
WHERE e.student_id = 1;

Результат:

title
Mathematics
History

Как найти всех студентов, записанных на конкретный курс?

Допустим, мы хотим узнать, кто записан на курс Mathematics (ID = 1):

SELECT s.name
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
WHERE e.course_id = 1;

Результат:

name
Alice
Charlie

Как найти студентов и их курсы?

Чтобы получить полную картину, кто на что записан, выполним запрос:

SELECT s.name AS student, c.title AS course
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id;

Результат:

student course
Alice Mathematics
Alice History
Bob Biology
Charlie Mathematics
Charlie History
Charlie Biology

Таблица enrollments делает нашу схему гибкой — мы можем без проблем добавлять или удалять связи между студентами и курсами, не трогая основные таблицы. Благодаря JOIN-запросам легко находить нужные данные, например, кто записан на какой курс. А внешние ключи автоматически следят за тем, чтобы не возникало ошибок — например, чтобы никто случайно не записал студента на курс, которого вообще не существует.

Типичные ошибки в связях "многие-ко-многим"

Отсутствие уникального ограничения: Если не установить PRIMARY KEY, можно случайно добавить одну и ту же связь несколько раз.

Нарушение целостности данных: Попытка вставить запись с несуществующим student_id или course_id приведёт к ошибке.

Неправильный порядок удаления данных: Если сначала удалить курс из courses, записи в enrollments останутся "сиротами". Для предотвращения этого используйте ON DELETE CASCADE в определении внешних ключей.

2
Задача
SQL SELF, 19 уровень, 4 лекция
Недоступна
Создание таблиц для связи "многие ко многим"
Создание таблиц для связи "многие ко многим"
2
Задача
SQL SELF, 19 уровень, 4 лекция
Недоступна
Получение связей между пользователями и проектами
Получение связей между пользователями и проектами
1
Опрос
Внешние ключи, 19 уровень, 4 лекция
Недоступен
Внешние ключи
Внешние ключи
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 64
13 сентября 2025
ТЕСТ: Какой столбец таблицы чаще всего ссылается внешний ключ? На какой? В каком? Что? перепишите эту чушь. Какая связь описывается как "многие-к-одному"? В вариантах нет ни одного варианта М:1, зато два варианта 1:М (хоть один и звучит по-идиотски) Какие два поля обычно включены в промежуточную таблицу? Среди вариантов: - Два первичных ключа из родительских таблиц - Два внешних ключа, ссылающихся на первичные ключи родительских таблиц Не находите, что это ОДИНАКОВЫЕ варианты?
30 июля 2025
Уважаемые разработчики курса. Тема лекций по связям не раскрыта. Пожалуйста, доработайте теорию связей. Это наиважнейшая тема.
Ra Уровень 35 Student
30 июля 2025
Теория по ключам что-то слабее других: 0. Как создавать 1-к-многим и как многие-к-1 1. Разжевать в теории понятия дочерняя и родительская таблица. 2. PRIMARY KEY (student_id, course_id) - это раньше не обьяснялось, хотелось бы поподробнее 3. Аналогично формат [FOREIGN KEY] CREATE TABLE user_projects ( user_id INT NOT NULL, project_id INT NOT NULL, PRIMARY KEY (user_id, project_id), FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ); 4. Какой столбец таблицы чаще всего ссылается внешний ключ? - это на русском? 🤣