ER Diagrams   «Prev  Next»

Lesson 11Resolving many-to-many relationships
ObjectiveImplement a many-to-many relationship with an associative table, two foreign keys and an appropriate uniqueness rule.

Convert a Many-to-Many Relationship into Two One-to-Many Relationships

To implement a many-to-many relationship in relational tables, create an associative table that records each pairing. For students and classes, use Enrollment: each row identifies one student enrolled in one class. Student and Class retain their own facts, while Enrollment holds the references and facts about the enrollment.

The business relationship remains many-to-many. Its table representation uses two one-to-many relationships: Student to Enrollment, and Class to Enrollment. This resolves the storage problems examined in Lesson 10.

Define What One Enrollment Means

In this example, ClassID identifies a particular scheduled class offering, and a student can have at most one Enrollment row for that offering. Students may exist before enrolling, and classes may exist before anyone enrolls. If your system records repeated attempts, terms or enrollment history separately, define those identities before choosing the key.

  1. Confirm both directions: one student can take several classes; one class can have several students.
  2. Create Enrollment: use a name that describes the association rather than an arbitrary Table C.
  3. Add StudentID and ClassID: both are required foreign keys referencing the appropriate parent.
  4. Make the pair unique: use a composite primary key on (StudentID, ClassID) for the stated rule.
  5. Add enrollment facts: EnrolledOn and Grade describe the pairing. Student names and class names stay in their parent tables.
  6. Check optionality, deletion rules and access patterns: these are separate decisions from choosing the primary key.

Read the Resolved Diagram in Both Directions

Student and Class each have a one-to-many relationship with Enrollment. Enrollment has the composite primary key StudentID and ClassID, two required foreign keys, EnrolledOn and Grade. Example pairs show one student in two classes and one class with two students.
Enrollment resolves the M:N association into two 1:N relationships. A parent can have zero or many enrollments; each enrollment references exactly one student and one class. PK means primary key; FK means foreign key.
  • Student → Enrollment: one student has zero, one or many enrollment rows. Each enrollment belongs to exactly one student.
  • Class → Enrollment: one class has zero, one or many enrollment rows. Each enrollment belongs to exactly one class.

A student with no classes is represented by a Student row and no Enrollment rows, not an Enrollment row with a NULL ClassID. Required foreign keys enforce the references in existing enrollment rows; they do not force every parent to have a child row.

Create the Three Tables

The following SQL uses explicit identifiers rather than vendor-specific automatic numbering. Run it in an empty practice schema. Grade is nullable because a new enrollment may not yet have a grade.

CREATE TABLE Student (
    StudentID INT NOT NULL PRIMARY KEY,
    LastName VARCHAR(60) NOT NULL,
    FirstName VARCHAR(60) NOT NULL
);

CREATE TABLE Class (
    ClassID INT NOT NULL PRIMARY KEY,
    ClassName VARCHAR(120) NOT NULL,
    ClassDept VARCHAR(100)
);

CREATE TABLE Enrollment (
    StudentID INT NOT NULL,
    ClassID INT NOT NULL,
    EnrolledOn DATE NOT NULL,
    Grade VARCHAR(5),
    CONSTRAINT pk_enrollment PRIMARY KEY (StudentID, ClassID),
    CONSTRAINT fk_enrollment_student
        FOREIGN KEY (StudentID) REFERENCES Student(StudentID),
    CONSTRAINT fk_enrollment_class
        FOREIGN KEY (ClassID) REFERENCES Class(ClassID)
);

CREATE INDEX ix_enrollment_class ON Enrollment(ClassID);

The primary key prevents a duplicate pair. It does not make StudentID individually unique or ClassID individually unique. Repeated individual identifiers are essential to representing many-to-many associations.

The example does not request cascading deletes. With foreign-key enforcement enabled, a referenced parent cannot simply be deleted while its enrollments remain. Choose retention and deletion policies deliberately; deleting an enrollment itself does not delete its parents.

Many database engines support the composite primary key with an index whose leading column is StudentID. In such an engine, another index on StudentID alone may be unnecessary. The ClassID index supports lookups from the other direction; inspect your engine's indexes and query plans before adding more.

Insert and Read the Pairings

INSERT INTO Student (StudentID, LastName, FirstName) VALUES
    (53985, 'Jones', 'Cindy'),
    (62292, 'Cruz', 'Leo');

INSERT INTO Class (ClassID, ClassName, ClassDept) VALUES
    (854, 'Finance', 'Business'),
    (768, 'Statistics', 'Math');

INSERT INTO Enrollment (StudentID, ClassID, EnrolledOn, Grade) VALUES
    (53985, 854, '2026-09-01', 'A'),
    (53985, 768, '2026-09-01', 'B'),
    (62292, 854, '2026-09-02', 'A');

SELECT s.FirstName, s.LastName, c.ClassName, e.Grade
FROM Enrollment AS e
JOIN Student AS s ON s.StudentID = e.StudentID
JOIN Class AS c ON c.ClassID = e.ClassID
ORDER BY s.StudentID, c.ClassID;
Result of the joined query
FirstNameLastNameClassNameGrade
CindyJonesStatisticsB
CindyJonesFinanceA
LeoCruzFinanceA

This is a three-row subset of the earlier lessons' enrollment example, with illustrative dates and grades added. Cindy appears twice in the report because she has two enrollments. Her name is still stored once in Student. Likewise, Finance is stored once in Class even though two students take it.

When a Surrogate Key Is Useful

An associative entity[1] does not have to use a composite primary key. A separate EnrollmentID can be useful when other tables must reference a particular enrollment. It does not, by itself, prevent duplicate student/class pairs.

The following is an alternative Enrollment definition, not a second table to create alongside the first. It uses the same parent tables. Supply unique EnrollmentID values, or adapt that column to your engine's identity or sequence syntax.

CREATE TABLE Enrollment (
    EnrollmentID INT NOT NULL PRIMARY KEY,
    StudentID INT NOT NULL,
    ClassID INT NOT NULL,
    EnrolledOn DATE NOT NULL,
    Grade VARCHAR(5),
    CONSTRAINT uq_enrollment_pair UNIQUE (StudentID, ClassID),
    CONSTRAINT fk_enrollment_student
        FOREIGN KEY (StudentID) REFERENCES Student(StudentID),
    CONSTRAINT fk_enrollment_class
        FOREIGN KEY (ClassID) REFERENCES Class(ClassID)
);

Under the same business rule, both designs enforce the same pair uniqueness. Adding more enrollment attributes is not, by itself, a reason to replace the composite key[2]. If multiple attempts for the same class offering are allowed, the row identity and uniqueness rule must include the relevant attempt or event; merely adding a surrogate key leaves that rule undefined.

Keep Additional Rules Separate

Verify the Design Before Moving On

A composite primary key[3] identifies the pairing; foreign keys validate its members. Neither constraint alone expresses every business rule. The next lesson develops mandatory and optional participation.

Resolving Many-to-Many Relationships - Exercise

Practice choosing the associative entity and placing its keys in the Resolving Many-to-Many Relationships exercise.

[1] Associative entity: An entity representing an association between other entities; also called a junction, linking or composite entity.
[2] Composite key: A key consisting of more than one attribute. Its combination, rather than each component individually, identifies a row.
[3] Composite primary key: A multiple-column key selected as the table's primary key. An associative table can instead use a surrogate primary key and a separate uniqueness constraint.