Ответ
Связь 'многие-ко-многим' (Many-to-Many) реализуется через промежуточную (связующую) таблицу (junction table). Эта таблица содержит внешние ключи, которые ссылаются на первичные ключи двух связываемых таблиц.
Ключевые принципы:
- Создание связующей таблицы: Она содержит как минимум два поля — внешние ключи (
FOREIGN KEY) для каждой из основных таблиц. - Составной первичный ключ: Для обеспечения уникальности пар связей (например, чтобы один студент не был записан на один и тот же курс дважды), для этих двух внешних ключей часто создается составной первичный ключ (
PRIMARY KEY).
Пример на SQL (PostgreSQL)
Предположим, у нас есть студенты и курсы. Один студент может посещать много курсов, и на одном курсе может быть много студентов.
-- Таблица студентов
CREATE TABLE Students (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- Таблица курсов
CREATE TABLE Courses (
id SERIAL PRIMARY KEY,
title VARCHAR(100) NOT NULL
);
-- Промежуточная таблица для связи Students и Courses
CREATE TABLE StudentCourses (
student_id INT REFERENCES Students(id) ON DELETE CASCADE,
course_id INT REFERENCES Courses(id) ON DELETE CASCADE,
-- Составной первичный ключ для уникальности пары (студент, курс)
PRIMARY KEY (student_id, course_id)
);
Пример в ORM (SQLAlchemy для Python)
В ORM-системах эта связь настраивается через специальный атрибут (secondary), который указывает на связующую таблицу.
from sqlalchemy import Column, Integer, String, Table, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
# Описание промежуточной таблицы без создания отдельного класса модели
student_courses_table = Table('student_courses',
Base.metadata,
Column('student_id', Integer, ForeignKey('students.id'), primary_key=True),
Column('course_id', Integer, ForeignKey('courses.id'), primary_key=True)
)
class Student(Base):
__tablename__ = 'students'
id = Column(Integer, primary_key=True)
name = Column(String)
# Связь с Course через таблицу student_courses_table
courses = relationship("Course", secondary=student_courses_table, back_populates="students")
class Course(Base):
__tablename__ = 'courses'
id = Column(Integer, primary_key=True)
title = Column(String)
# Обратная связь для удобства запросов
students = relationship("Student", secondary=student_courses_table, back_populates="courses")