Зв'язок "багато-до-багатьох" — це коли один запис в одній таблиці може бути пов'язаний з кількома записами в іншій таблиці, і навпаки. Наприклад: - Один студент (з таблиці 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 у визначенні зовнішніх ключів.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ