Popular Searches
Popular Course Categories
Popular Courses

Understanding Many-to-Many Relationships

Understanding Many-to-Many Relationships

Database Design and Normalization

Understanding Many-to-Many Relationships in MySQL

Course: MySQL Training

Topic: Understanding Many-to-Many Relationships

Course Link: MySQL Training Course

Demo Link: Register for a Course Demo


1. Introduction to Many-to-Many Relationships

A many-to-many relationship is a type of relationship in a relational database in which multiple records in one table can be associated with multiple records in another table. It is commonly used when both entities can have several related records.

For example, in a college database, one student can enroll in multiple courses, and each course can have multiple students. This creates a many-to-many relationship between the students table and the courses table.

In MySQL, a many-to-many relationship is generally implemented using a third table called a junction table, also known as a bridge table, linking table, mapping table, or associative table. The junction table stores foreign keys referencing the two related tables.

Learning Objectives

  • Understand the meaning and purpose of many-to-many relationships.
  • Identify real-world examples of many-to-many relationships.
  • Understand why a junction table is required.
  • Learn how primary keys and foreign keys connect multiple tables.
  • Create a junction table using MySQL.
  • Insert, retrieve, update, and delete related records.
  • Use INNER JOIN and LEFT JOIN to retrieve related information.
  • Understand composite primary keys and unique constraints.
  • Apply referential integrity and normalization principles.
  • Identify common errors and follow database design best practices.

2. What Is a Many-to-Many Relationship?

A many-to-many relationship exists when one record in a table can be associated with many records in another table, and one record in the second table can also be associated with many records in the first table.

Consider students and courses:

  • One student can enroll in multiple courses.
  • One course can contain multiple students.
  • Each enrollment connects one student to one course.
  • The collection of enrollments records all student-course associations.

Example

Student ID Student Name Enrolled Courses
1 Aarav MySQL, Python
2 Priya MySQL, Java
3 Rahul Python, Java

In this example, MySQL has more than one student, and Aarav has more than one course. Both sides support multiple associations, which makes the relationship many-to-many.

3. Real-World Examples of Many-to-Many Relationships

3.1 Students and Courses

A student can enroll in several courses, while each course can have many students. A junction table such as student_courses records each enrollment.

3.2 Products and Orders

An order can contain multiple products, and the same product can appear in many orders. An order_items table connects orders with products and stores details such as quantity and purchase price.

3.3 Authors and Books

An author may write multiple books, and a book may have multiple authors. An author_books table records these associations.

3.4 Employees and Projects

An employee can work on multiple projects, and each project can have multiple employees. An employee_projects table records project assignments.

3.5 Movies and Actors

A movie can feature multiple actors, and an actor can appear in multiple movies. A movie_cast table can connect actors and movies.

3.6 Blog Posts and Tags

A post can have multiple tags, and a tag can be applied to multiple posts. A post_tags table connects posts and tags.

4. Why Is a Junction Table Necessary?

In a relational database, a single foreign key in one table generally represents a many-to-one relationship from the referencing table to the referenced table. It does not directly represent unrestricted many-to-many associations.

A junction table converts a many-to-many relationship into two one-to-many relationships:

  • One student can have many enrollment records.
  • One course can have many enrollment records.
  • Each enrollment references one student and one course.

Incorrect Design

Storing multiple course IDs in one student column is not a good relational design.

CREATE TABLE students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(100),
    course_ids VARCHAR(255)
);

A value such as '101,102,103' in course_ids makes individual course references difficult to validate, join, index, update, and enforce with foreign keys.

Correct Design

Create separate tables for students and courses, then connect them through a junction table.

students
    |
    | one-to-many
    |
student_courses
    |
    | many-to-one
    |
courses

This design stores each association as a separate row and supports database constraints.

5. Understanding the Structure of a Junction Table

A junction table contains foreign keys that reference the primary keys of the two related tables. It can also contain attributes describing the relationship itself.

For a student-course relationship, the junction table might contain:

Column Purpose
student_id References a student.
course_id References a course.
enrollment_date Records when the student enrolled.
grade Stores the student's grade for that course.
status Records enrollment status, such as active or completed.

Each row represents one enrollment. The junction table can contain additional columns when the relationship has its own information.

6. Understanding Primary Keys and Foreign Keys

6.1 Primary Key

A primary key uniquely identifies each record in a table. It cannot contain NULL values, and its values must be unique.

CREATE TABLE students (
    student_id INT AUTO_INCREMENT PRIMARY KEY,
    student_name VARCHAR(100) NOT NULL
);

6.2 Foreign Key

A foreign key references a key in another table. It helps maintain referential integrity by preventing invalid references under the configured constraint rules.

CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    FOREIGN KEY (student_id)
        REFERENCES students(student_id),
    FOREIGN KEY (course_id)
        REFERENCES courses(course_id)
);

The junction table has two foreign keys: one for students and one for courses. Both references must point to valid records when foreign key enforcement is active.

6.3 Composite Primary Key

A composite primary key contains two or more columns. In a junction table, the combination of the two foreign keys can serve as the primary key.

CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    PRIMARY KEY (student_id, course_id)
);

The composite key prevents the same student-course combination from being inserted more than once. A student can still enroll in multiple different courses, and each course can still have multiple students.

7. Creating a Many-to-Many Relationship in MySQL

In this section, we will create a complete example involving students, courses, and a junction table called student_courses.

Step 1: Create the Database

CREATE DATABASE college_db;
USE college_db;

Step 2: Create the Students Table

CREATE TABLE students (
    student_id INT AUTO_INCREMENT PRIMARY KEY,
    student_name VARCHAR(100) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    admission_date DATE
) ENGINE=InnoDB;

This table stores student information. The student_id column uniquely identifies each student, while the email column prevents duplicate email addresses.

Step 3: Create the Courses Table

CREATE TABLE courses (
    course_id INT AUTO_INCREMENT PRIMARY KEY,
    course_name VARCHAR(100) NOT NULL,
    course_fee DECIMAL(10,2) NOT NULL DEFAULT 0.00
) ENGINE=InnoDB;

This table stores course information. Each course receives a unique ID, name, and fee.

Step 4: Create the Junction Table

CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrollment_date DATE NOT NULL,
    grade VARCHAR(5),
    status VARCHAR(20) NOT NULL DEFAULT 'Active',
    PRIMARY KEY (student_id, course_id),
    CONSTRAINT fk_sc_student
        FOREIGN KEY (student_id)
        REFERENCES students(student_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    CONSTRAINT fk_sc_course
        FOREIGN KEY (course_id)
        REFERENCES courses(course_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
) ENGINE=InnoDB;

Explanation of the Table Definition

  • student_id references the students table.
  • course_id references the courses table.
  • enrollment_date records the enrollment date.
  • grade stores the student's result for the course.
  • status stores the enrollment status.
  • PRIMARY KEY (student_id, course_id) prevents duplicate student-course pairs.
  • ON DELETE CASCADE removes related enrollment rows when a student or course is deleted.

Use cascading deletes only when removing related enrollment records is consistent with the application's data-retention requirements.

8. Inserting Data into the Tables

8.1 Insert Students

INSERT INTO students
    (student_name, email, admission_date)
VALUES
    ('Aarav Sharma', '[email protected]', '2026-01-10'),
    ('Priya Mehta', '[email protected]', '2026-01-12'),
    ('Rahul Verma', '[email protected]', '2026-01-15'),
    ('Neha Patel', '[email protected]', '2026-01-18');

8.2 Insert Courses

INSERT INTO courses (course_name, course_fee)
VALUES
    ('MySQL Training', 8000.00),
    ('Python Programming', 10000.00),
    ('Java Development', 12000.00);

8.3 Insert Student-Course Associations

INSERT INTO student_courses
    (student_id, course_id, enrollment_date, grade, status)
VALUES
    (1, 1, '2026-02-01', 'A', 'Active'),
    (1, 2, '2026-02-03', 'B+', 'Active'),
    (2, 1, '2026-02-02', 'A-', 'Active'),
    (2, 3, '2026-02-05', 'B', 'Active'),
    (3, 2, '2026-02-06', NULL, 'Active'),
    (3, 3, '2026-02-08', 'A', 'Completed'),
    (4, 1, '2026-02-10', NULL, 'Active');

Student 1 is enrolled in two courses. Course 1 has three enrolled students. This demonstrates the many-to-many relationship.

9. Viewing Data in the Three Tables

9.1 Display All Students

SELECT * FROM students;

9.2 Display All Courses

SELECT * FROM courses;

9.3 Display All Enrollments

SELECT * FROM student_courses;

The junction table displays the associations between students and courses. A row in this table does not duplicate all the student or course details; it stores their identifiers and enrollment-specific information.

10. Retrieving Data with INNER JOIN

To retrieve information from a many-to-many relationship, join the first table to the junction table and then join the junction table to the second table.

10.1 Display Students and Their Courses

SELECT
    s.student_id,
    s.student_name,
    c.course_name,
    sc.enrollment_date,
    sc.grade,
    sc.status
FROM students AS s
INNER JOIN student_courses AS sc
    ON s.student_id = sc.student_id
INNER JOIN courses AS c
    ON sc.course_id = c.course_id
ORDER BY s.student_id, c.course_name;

This query returns one row for every matching student-course enrollment. A student enrolled in three courses will appear in three result rows.

10.2 Display Courses and Their Students

SELECT
    c.course_id,
    c.course_name,
    s.student_name,
    sc.enrollment_date,
    sc.grade
FROM courses AS c
INNER JOIN student_courses AS sc
    ON c.course_id = sc.course_id
INNER JOIN students AS s
    ON sc.student_id = s.student_id
ORDER BY c.course_name, s.student_name;

This query starts from the courses table and displays the students associated with each course.

11. Using LEFT JOIN to Find Records Without Associations

INNER JOIN returns matching records only. LEFT JOIN is useful when you need to include students or courses that have no associated records in the junction table.

11.1 Find Students Who Have Not Enrolled in Any Course

SELECT
    s.student_id,
    s.student_name
FROM students AS s
LEFT JOIN student_courses AS sc
    ON s.student_id = sc.student_id
WHERE sc.student_id IS NULL;

11.2 Find Courses with No Students

SELECT
    c.course_id,
    c.course_name
FROM courses AS c
LEFT JOIN student_courses AS sc
    ON c.course_id = sc.course_id
WHERE sc.course_id IS NULL;

11.3 Display Every Course and Its Student Count

SELECT
    c.course_id,
    c.course_name,
    COUNT(sc.student_id) AS total_students
FROM courses AS c
LEFT JOIN student_courses AS sc
    ON c.course_id = sc.course_id
GROUP BY c.course_id, c.course_name
ORDER BY c.course_name;

Using LEFT JOIN ensures that courses with no enrollments are included with a student count of zero.

12. Retrieving Specific Associations

12.1 Find Courses for a Particular Student

SELECT
    s.student_name,
    c.course_name,
    sc.enrollment_date
FROM students AS s
JOIN student_courses AS sc
    ON s.student_id = sc.student_id
JOIN courses AS c
    ON sc.course_id = c.course_id
WHERE s.student_id = 1;

12.2 Find Students Enrolled in MySQL Training

SELECT
    s.student_id,
    s.student_name,
    s.email
FROM students AS s
JOIN student_courses AS sc
    ON s.student_id = sc.student_id
JOIN courses AS c
    ON sc.course_id = c.course_id
WHERE c.course_name = 'MySQL Training';

12.3 Find Students Enrolled in More Than One Course

SELECT
    s.student_id,
    s.student_name,
    COUNT(sc.course_id) AS total_courses
FROM students AS s
JOIN student_courses AS sc
    ON s.student_id = sc.student_id
GROUP BY s.student_id, s.student_name
HAVING COUNT(sc.course_id) > 1;

12.4 Find Courses with More Than Two Students

SELECT
    c.course_id,
    c.course_name,
    COUNT(sc.student_id) AS total_students
FROM courses AS c
JOIN student_courses AS sc
    ON c.course_id = sc.course_id
GROUP BY c.course_id, c.course_name
HAVING COUNT(sc.student_id) > 2;

13. Updating Records in a Many-to-Many Relationship

When updating a relationship, the change usually takes place in the junction table. For example, a student's grade or enrollment status belongs to the student's enrollment in a particular course.

13.1 Update a Grade

UPDATE student_courses
SET grade = 'A+'
WHERE student_id = 1
  AND course_id = 1;

13.2 Change an Enrollment Status

UPDATE student_courses
SET status = 'Completed'
WHERE student_id = 1
  AND course_id = 2;

13.3 Change the Enrollment Date

UPDATE student_courses
SET enrollment_date = '2026-02-15'
WHERE student_id = 2
  AND course_id = 1;

Because the composite primary key consists of both IDs, use both columns in the WHERE clause when updating a specific association.

14. Deleting Records from a Junction Table

Deleting a row from a junction table removes the association between two records. It does not necessarily delete the student or course themselves.

14.1 Remove a Student from a Course

DELETE FROM student_courses
WHERE student_id = 1
  AND course_id = 2;

This deletes the enrollment of student 1 in course 2. The student and course records remain in their respective tables.

14.2 Delete a Student

DELETE FROM students
WHERE student_id = 4;

With the cascading foreign key used in this tutorial, the student's associated enrollment rows are automatically removed.

14.3 Delete a Course

DELETE FROM courses
WHERE course_id = 3;

The course's associated enrollment rows are also removed because the junction table defines ON DELETE CASCADE for the course foreign key.

15. Composite Primary Key vs. Separate Primary Key

There are two common approaches to identifying rows in a junction table.

15.1 Composite Primary Key

CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    PRIMARY KEY (student_id, course_id)
);

This design prevents duplicate student-course pairs directly. It is a natural choice when each student can enroll in a particular course only once.

15.2 Separate Primary Key with a UNIQUE Constraint

CREATE TABLE student_courses (
    enrollment_id INT AUTO_INCREMENT PRIMARY KEY,
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrollment_date DATE NOT NULL,
    UNIQUE (student_id, course_id),
    FOREIGN KEY (student_id)
        REFERENCES students(student_id),
    FOREIGN KEY (course_id)
        REFERENCES courses(course_id)
);

This design assigns an independent ID to each enrollment. The UNIQUE constraint still prevents the same student-course pair from being entered more than once.

Feature Composite Primary Key Separate Primary Key
Unique row identity Uses both foreign keys. Uses an independent enrollment ID.
Prevents duplicate pairs Yes. Yes, when a UNIQUE constraint is added.
Useful for Simple association records. Associations referenced by other tables or application features.
Additional identifier Not required. Required.

Choose the key design based on whether the association needs its own identity and whether the business rules permit repeated associations over time.

16. Storing Additional Information in a Junction Table

A junction table can store attributes that describe the relationship between the two entities. This is particularly important in real-world applications.

For example, the relationship between a student and a course may contain enrollment date, grade, payment status, or completion status. These details describe the student's enrollment in that course rather than the student or course in isolation.

CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrollment_date DATE NOT NULL,
    grade VARCHAR(5),
    payment_status VARCHAR(20) DEFAULT 'Pending',
    completion_status VARCHAR(20) DEFAULT 'In Progress',
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id)
        REFERENCES students(student_id),
    FOREIGN KEY (course_id)
        REFERENCES courses(course_id)
);

Other examples include quantity and purchase price in an order item, assignment date in an employee-project relationship, or character name in a movie-actor relationship.

17. Many-to-Many Relationships and Database Normalization

Normalization is the process of organizing relational data to reduce unnecessary duplication and improve consistency. Junction tables help represent many-to-many relationships without storing lists of related IDs in a single column.

Problems with Unnormalized Data

  • Multiple IDs stored in a single column are difficult to validate.
  • Updating one association may require changing a text list.
  • Filtering and joining individual relationships becomes more complicated.
  • Foreign key constraints cannot straightforwardly validate each ID inside a comma-separated list.
  • Duplicate associations and inconsistent values are harder to prevent.

Benefits of a Junction Table

  • Each association is stored as a separate row.
  • Foreign keys validate the referenced records.
  • Primary keys or unique constraints prevent unwanted duplicate associations.
  • Queries can filter and aggregate associations efficiently.
  • Relationship-specific information can be stored in appropriate columns.

18. Referential Integrity and Foreign Key Actions

Referential integrity ensures that the IDs stored in a junction table refer to valid records in the related tables. MySQL supports foreign key constraints for this purpose when using a compatible storage engine such as InnoDB.

Action Description
ON DELETE CASCADE Deletes matching junction rows when the referenced parent row is deleted.
ON DELETE RESTRICT Prevents deleting a parent when dependent rows exist.
ON DELETE NO ACTION In InnoDB, this behaves like RESTRICT.
ON DELETE SET NULL Sets the foreign key to NULL when supported and when the column permits NULL; it may not suit a required junction association.
ON UPDATE CASCADE Updates matching foreign key values when the referenced key value changes.

In most designs, primary keys are stable and are not changed frequently. Select referential actions according to the business rules and data-retention requirements.

19. Indexing Junction Tables for Better Performance

Indexes help MySQL find records efficiently. Junction tables are frequently queried by either foreign key, so their index design should reflect common query patterns.

A composite primary key such as (student_id, course_id) creates an index that is useful for searching by student_id or by both columns together. It does not provide the same leading-column lookup advantage for a search using only course_id.

Consider adding an index on course_id when course-based lookups are common and an appropriate index is not already available.

CREATE INDEX idx_student_courses_course
ON student_courses(course_id);

Check the Query Execution Plan

EXPLAIN
SELECT
    s.student_name,
    c.course_name
FROM student_courses AS sc
JOIN students AS s
    ON sc.student_id = s.student_id
JOIN courses AS c
    ON sc.course_id = c.course_id
WHERE sc.course_id = 1;

Use EXPLAIN to inspect how MySQL plans to execute a query. Avoid adding indexes without a reason because every additional index uses storage and adds overhead to data changes.

20. Common Mistakes in Many-to-Many Relationships

Mistake Problem Solution
Storing multiple IDs in one column Individual values are difficult to validate and query. Create a junction table.
Forgetting foreign keys Invalid references may be stored. Declare foreign key constraints.
Allowing duplicate pairs The same association can appear multiple times unintentionally. Use a composite primary key or a UNIQUE constraint.
Using incorrect column types Foreign key creation can fail if referenced types are incompatible. Match the relevant data types and signedness.
Using INNER JOIN when unmatched records are needed Records without associations disappear from the result. Use LEFT JOIN when all parent records must appear.
Deleting parent records without reviewing dependencies Cascading rules may remove many related associations. Choose referential actions carefully.
Missing an index for reverse lookups Queries filtering by the second foreign key may be less efficient. Review query patterns and index requirements.
Putting unrelated attributes in the junction table Data ownership becomes unclear and duplication may increase. Store each attribute with the entity or relationship it describes.

21. Troubleshooting Common MySQL Errors

21.1 Duplicate Entry for a Primary Key

Cause: The same combination of student ID and course ID already exists in the junction table.

INSERT INTO student_courses
    (student_id, course_id, enrollment_date)
VALUES
    (1, 1, '2026-03-01');

If the pair (1, 1) already exists, the composite primary key rejects the duplicate.

Solution: Check whether the association exists before inserting. Update the existing row when the intention is to change enrollment details.

SELECT *
FROM student_courses
WHERE student_id = 1
  AND course_id = 1;

21.2 Foreign Key Constraint Failure

Cause: A student or course ID does not exist in its parent table, or the foreign key definition is incompatible with the referenced key.

Solution: Verify the parent record and check the table definitions.

SELECT student_id
FROM students
WHERE student_id = 100;

SELECT course_id
FROM courses
WHERE course_id = 100;

21.3 Cannot Create a Foreign Key

Possible causes:

  • The referenced column is not a suitable indexed key.
  • The referencing and referenced column types are incompatible.
  • The tables use incompatible storage engines.
  • The referenced table or column does not exist.
  • The constraint name conflicts with another constraint name in the schema.

Solution: Review the exact MySQL error message, confirm table definitions, and ensure the referenced columns have appropriate keys and compatible data types.

21.4 Unexpectedly Missing Rows in a Query

Cause: INNER JOIN returns only rows with matching associations, so students without enrollments or courses without students are omitted.

Solution: Use LEFT JOIN when unmatched records must remain in the result.

22. Using Transactions for Multiple Relationship Changes

Transactions allow related database operations to be committed as a single logical unit. For example, a system may insert an enrollment and update another related record in one transaction.

START TRANSACTION;

INSERT INTO student_courses
    (student_id, course_id, enrollment_date, status)
VALUES
    (2, 2, '2026-03-10', 'Active');

UPDATE courses
SET course_fee = course_fee
WHERE course_id = 2;

COMMIT;

The UPDATE statement above is only an illustrative placeholder operation; a real application should replace it with an actual required business change or omit it. If an operation fails, the application should handle the error and issue ROLLBACK before committing any incomplete transaction.

START TRANSACTION;

INSERT INTO student_courses
    (student_id, course_id, enrollment_date, status)
VALUES
    (2, 2, '2026-03-10', 'Active');

-- If a later operation fails:
ROLLBACK;

Use transaction handling that matches the application's actual error-handling flow. In production code, do not blindly execute both ROLLBACK and COMMIT for the same failed operation.

23. Practical Project: Online Shopping Order Management

Project Objective: Build a database that connects orders and products through an order_items junction table.

Project Requirements

  • One order can contain multiple products.
  • One product can appear in multiple orders.
  • Each order-product combination should be stored as a separate row.
  • The junction table must record product quantity and purchase price.
  • Foreign keys must maintain referential integrity.
  • The design must support queries for order contents and product sales.

Step 1: Create the Orders Table

CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_name VARCHAR(100) NOT NULL,
    order_date DATE NOT NULL
) ENGINE=InnoDB;

Step 2: Create the Products Table

CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    current_price DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB;

Step 3: Create the Order Items Junction Table

CREATE TABLE order_items (
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    unit_price_at_purchase DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (order_id, product_id),
    CONSTRAINT fk_order_items_order
        FOREIGN KEY (order_id)
        REFERENCES orders(order_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_order_items_product
        FOREIGN KEY (product_id)
        REFERENCES products(product_id)
        ON DELETE RESTRICT,
    CHECK (quantity > 0),
    CHECK (unit_price_at_purchase >= 0)
) ENGINE=InnoDB;

The composite primary key prevents the same product from appearing in two separate rows of the same order. If the business permits multiple separate lines for the same product within an order, use a separate order-item ID or line number instead.

Step 4: Insert Orders

INSERT INTO orders (customer_name, order_date)
VALUES
    ('Amit Shah', '2026-03-01'),
    ('Sara Khan', '2026-03-02'),
    ('Neha Patel', '2026-03-03');

Step 5: Insert Products

INSERT INTO products (product_name, current_price)
VALUES
    ('Keyboard', 1200.00),
    ('Mouse', 600.00),
    ('Monitor', 8500.00);

Step 6: Insert Order Items

INSERT INTO order_items
    (order_id, product_id, quantity, unit_price_at_purchase)
VALUES
    (1, 1, 1, 1200.00),
    (1, 2, 2, 600.00),
    (2, 2, 1, 600.00),
    (2, 3, 1, 8500.00),
    (3, 1, 2, 1200.00);

Step 7: Display Each Order and Its Products

SELECT
    o.order_id,
    o.customer_name,
    o.order_date,
    p.product_name,
    oi.quantity,
    oi.unit_price_at_purchase,
    oi.quantity * oi.unit_price_at_purchase AS line_total
FROM orders AS o
JOIN order_items AS oi
    ON o.order_id = oi.order_id
JOIN products AS p
    ON oi.product_id = p.product_id
ORDER BY o.order_id, p.product_name;

Step 8: Calculate the Total Value of Each Order

SELECT
    o.order_id,
    o.customer_name,
    SUM(oi.quantity * oi.unit_price_at_purchase) AS order_total
FROM orders AS o
JOIN order_items AS oi
    ON o.order_id = oi.order_id
GROUP BY o.order_id, o.customer_name
ORDER BY o.order_id;

Step 9: Calculate Total Quantity Sold for Each Product

SELECT
    p.product_id,
    p.product_name,
    SUM(oi.quantity) AS total_quantity_sold
FROM products AS p
JOIN order_items AS oi
    ON p.product_id = oi.product_id
GROUP BY p.product_id, p.product_name
ORDER BY total_quantity_sold DESC;

Project Outcome

This project demonstrates how a junction table connects orders and products. It also shows how relationship-specific attributes, such as quantity and the price at purchase time, belong in the junction table.

24. Practice Exercises

  1. Create authors and books tables and connect them through an author_books junction table.
  2. Create employees and projects tables with an employee_projects junction table.
  3. Use a composite primary key to prevent duplicate employee-project assignments.
  4. Insert at least five records in each parent table and several associations in the junction table.
  5. Write a query that displays each employee with all assigned projects.
  6. Find projects that do not have any employees assigned.
  7. Find employees assigned to more than two projects.
  8. Add an assignment date column to the junction table.
  9. Update one association's details without changing the parent records.
  10. Delete one association and verify that the parent records remain in the database.
  11. Compare a composite primary key with a separate junction-table primary key.
  12. Explain why comma-separated IDs are not a suitable replacement for a junction table.

25. Interview Questions and Answers

Q1. What is a many-to-many relationship?

Answer: It is a relationship in which multiple records in one table can be associated with multiple records in another table.

Q2. How do you implement a many-to-many relationship in MySQL?

Answer: Create two entity tables and a junction table containing foreign keys that reference both entity tables.

Q3. What is a junction table?

Answer: A junction table stores the associations between records in two related tables. It may also contain attributes describing each association.

Q4. Why can't a single foreign key directly represent a many-to-many relationship?

Answer: A single foreign key in a child table normally allows each child row to reference one parent row. A junction table is required to record multiple associations in both directions.

Q5. What is a composite primary key?

Answer: A primary key made up of two or more columns. In a junction table, it often combines the foreign keys to prevent duplicate pairs.

Q6. Can a junction table have additional columns?

Answer: Yes. It can contain information about the association, such as enrollment date, grade, quantity, purchase price, or assignment status.

Q7. What is the difference between INNER JOIN and LEFT JOIN?

Answer: INNER JOIN returns matching rows only. LEFT JOIN returns all rows from the left table, including those without a matching row on the right.

Q8. How do you prevent duplicate associations?

Answer: Use a composite primary key containing both foreign keys or a UNIQUE constraint on their combination.

Q9. What does ON DELETE CASCADE do in a junction table?

Answer: It removes dependent association rows when a referenced parent row is deleted.

Q10. Why are indexes important in junction tables?

Answer: Indexes can improve joins and searches by foreign key values. Their design should reflect common query patterns.

Q11. Can a record exist without any junction-table associations?

Answer: Yes. Unless additional business rules prevent it, a student may exist without enrollments and a course may exist without students.

Q12. What is the difference between a junction table and a regular entity table?

Answer: A junction table primarily records relationships between entities, although it can also store relationship-specific attributes. A regular entity table primarily stores information about an entity itself.

26. Quick Revision Checklist

  • A many-to-many relationship allows multiple associations on both sides.
  • A junction table represents the relationship between two entity tables.
  • The junction table generally contains foreign keys referencing both parent tables.
  • A composite primary key or UNIQUE constraint can prevent duplicate associations.
  • Relationship-specific details belong in the junction table.
  • INNER JOIN retrieves matching associations.
  • LEFT JOIN can reveal parent records without associations.
  • Foreign keys help maintain referential integrity.
  • Indexes should support common joins and lookups.
  • Referential actions must match business and data-retention rules.
  • Transactions help manage related operations as a logical unit.
  • A junction table is more reliable than storing comma-separated IDs in one column.

27. Conclusion

Many-to-many relationships are an essential concept in MySQL database design. They are used when multiple records in one table can be associated with multiple records in another table, such as students and courses, products and orders, or employees and projects.

The standard solution is a junction table containing foreign keys to both related tables. A suitable primary key or UNIQUE constraint prevents duplicate associations, while foreign keys maintain referential integrity. JOIN operations make it possible to retrieve related information, and additional junction-table columns can store details about each relationship.

By practicing table creation, data insertion, joins, updates, deletes, indexing, and constraints, you can build reliable relational database structures for real-world applications.

Explore the course: MySQL Training at JustAcademy

Book a demo: Register for a Course Demo

whatsapp