| Lesson 11 | Resolving many-to-many relationships |
| Objective | Convert many-to-many relationships into one-to-many relationships. |
A many-to-many relationship is valid conceptually (Students ↔ Classes, Orders ↔ Products, Employees ↔ Projects) but cannot be implemented directly in a relational schema without inviting redundancy and anomalies. The standard resolution is to introduce an associative entity (also called a junction, linking, or composite entity) and replace the single M:N with two clean 1:N relationships.
ENROLLMENT, ORDER_ITEM, EMPLOYEE_PROJECT).A_id, B_id). Simple, prevents duplicates naturally.id, plus a unique constraint on (A_id, B_id) to prevent duplicates.Quantity, UnitPriceAtOrder, EnrolledOn, Role, Hours, etc.OrderID, ProductID).NOT NULL; optional participation can allow NULL only where meaningful.Example A - Orders and Products → ORDER_ITEMS
erDiagram
ORDERS ||--|{ ORDER_ITEMS : contains
PRODUCTS ||--|{ ORDER_ITEMS : appears_in
ORDERS { int order_id date order_date int customer_id }
PRODUCTS { int product_id string name float list_price }
ORDER_ITEMS { int order_id int product_id int quantity decimal(10,2) unit_price }
Example B - Students and Classes → ENROLLMENT
-- Composite-key variant (simple and duplicate-proof)
CREATE TABLE Student (
student_id INT PRIMARY KEY,
last_name VARCHAR(60) NOT NULL,
first_name VARCHAR(60) NOT NULL
);
CREATE TABLE Class (
class_id INT PRIMARY KEY,
class_name VARCHAR(120) NOT NULL
);
CREATE TABLE Enrollment (
student_id INT NOT NULL,
class_id INT NOT NULL,
enrolled_on DATE NOT NULL,
grade CHAR(2),
PRIMARY KEY (student_id, class_id),
FOREIGN KEY (student_id) REFERENCES Student(student_id) ON DELETE CASCADE,
FOREIGN KEY (class_id) REFERENCES Class(class_id) ON DELETE RESTRICT
);
CREATE INDEX ix_enrollment_student ON Enrollment(student_id);
CREATE INDEX ix_enrollment_class ON Enrollment(class_id);
Alternative (surrogate key) with uniqueness on the pair:
CREATE TABLE Enrollment (
enrollment_id INT PRIMARY KEY,
student_id INT NOT NULL,
class_id INT NOT NULL,
enrolled_on DATE NOT NULL,
grade CHAR(2),
CONSTRAINT uq_enrollment UNIQUE (student_id, class_id),
FOREIGN KEY (student_id) REFERENCES Student(student_id) ON DELETE CASCADE,
FOREIGN KEY (class_id) REFERENCES Class(class_id) ON DELETE RESTRICT
);
ClassName) into the associative entity; join to read the authoritative value.unit_price at order time) when history matters.Entities in a many-to-many relationship must be linked through a third entity, a composite entity[1] (associative entity). Its purpose is to represent the pairing between the two parents and to own attributes of that pairing. It commonly uses a composite key[2]; in table terms, this is a composite primary key[3].
After resolution, you have two 1:N relationships:
Occasionally the associative entity may “own” additional attributes (e.g., EnrolledOn, Grade) that logically describe the relationship rather than either parent. Avoid relocating core, authoritative attributes from parent entities without a clear rule.
(student_id, class_id)) to uniquely identify the pairing.