Script Valley
SQL for Developers (Not DBAs)
Schema Design and ConstraintsLesson 4.5

Many-to-many relationships and junction tables

many-to-many pattern, junction table, composite primary key vs surrogate, extra columns on junction, querying many-to-many, tag pattern

Many-to-Many Requires a Third Table

A student can enroll in many courses. A course can have many students. Neither table can hold the relationship directly — you need a third table, the junction table, where each row represents one pairing.

-- Junction table for students and courses
CREATE TABLE enrollments (
  student_id  INTEGER REFERENCES students(id) ON DELETE CASCADE,
  course_id   INTEGER REFERENCES courses(id)  ON DELETE CASCADE,
  enrolled_at TIMESTAMP DEFAULT NOW(),
  grade       NUMERIC(4,2),
  PRIMARY KEY (student_id, course_id)  -- composite PK prevents duplicate enrollment
);

Extra Columns on the Junction Table

Junction tables often carry relationship-specific data — enrollment date, grade, role in a project. This is normal and expected. The junction table is a first-class entity.

Querying Many-to-Many

-- All courses a student is enrolled in
SELECT c.title, e.grade
FROM courses c
JOIN enrollments e ON c.id = e.course_id
WHERE e.student_id = 42
ORDER BY e.enrolled_at;

Always add indexes on both foreign key columns in the junction table. Queries almost always filter by one side or the other.

Many-to-many relationships and junction tables — Schema Design and Constraints — SQL for Developers (Not DBAs) — Script Valley — Script Valley