views:

285

answers:

3

What is the best way of handling many-to-many relations in a RDBMS database like mySQL?

Have tried using a pivot table to keep track of the relationships, but it leads to either one of the following:

  • Normalization gets left behind

  • Columns that is empty or null

What approach have you taken in order to support many-to-many relationships?

+4  A: 

I would use a pivot table, but I don't see where your issues are coming from. Using a simple student/class example:

Student
-------
Id (Primary Key)
FirstName
LastName

Course
------
Id (Primary Key)
Title

StudentCourse
-------------
StudentId (Foreign Key -> Student)
CourseId (Foreign Key -> Course)

Or, as somebody else mentioned in response to your Student/Teacher/Course question (which would have an additional table to store the type of person in the course):

PersonType
----------
Id (Primary Key)
Type

Person
------
Id (Primary Key)
FirstName
LastName
Type (Foreign Key -> PersonType)

Course
------
Id (Primary Key)
Title

PersonCourse
------------
PersonId (Foreign Key -> Person)
CourseId (Foreign Key -> Course)

The Student table contains student information, the Course table stores course information...and the pivot table simply contains the Ids of the relevant students and courses. That shouldn't lead to any null/empty columns or anything.

Justin Niessner
Hi Justin, Thanks for your thoughts. If you would also have teachers that joins the courses (rename the StudentCourse to PeopleToCourses for logic) and have an additional column in it called TeacherId. In the case of no Students but with one Teacher attending, the "StudentId" column would be empty. Right?
Industrial
There's of course an option to make an additional table called TeachersCourse following your example, but with that in mind, additional joins would be required to get all the people for a course.
Industrial
In that case, I'd say you're using poor table design. If you wanted something to map teachers to courses, you should add another pivot table for lookups between teachers/courses instead of changing the name of the existing one and adding a column. You would then join the results from those two pivot tables to get a listing of all the people associated to a course.
Justin Niessner
Why is that? Wouldn't it be wise to keep the table count low together with the JOIN's?
Industrial
Unless every row in the pivot table has a student, teacher, and course associated with it...then you're de-normalizing your data and (in the case of large pivot tables) wasting space. If your data is properly indexed, that join shouldn't cause any issues for you.
Justin Niessner
In that case, put teachers and students in a single table called People then either 1) add a column called PeopleType that can hold "t" or "s" or 2) create a separate tables called Teachers and Students each table containing only a single column PeopleID. In either case, your registrations are handled using the linking table you have now, except StudentID becomes PeopleID.
Larry Lustig
@Larry - Also a good suggestion.
Justin Niessner
@Industrial - almost certianly you are going to hold different data about STUDENTS and TEACHERS. So you need two tables. From which it follows that you need two intersection (pivot) tables - STUDENT_COURSES and TEACHER_COURSES. The pain of the occasional extra join may well be less than the pain of fitting different records to procrustean table structures. Besides, a lot of the time you will be querying COURSES with just their TEACHERS or COURSES with just their STUDENTS rather than COURSES with all their PEOPLE.
APC
Well, both TEACHERS and STUDENTS will be linked up later on in the structure to their own separate information-holding tables. It comes down to the design then - how often both STUDENTS and TEACHERS are queried out together then :)
Industrial
@Industrial, I would change the question slightly. The question really is "how often are students and teachers more similar to each other than they are different?" Not knowing anything about your application I can't tell you the answer, but I suspect that if you begin to think through it you may discover they are more alike than you first thought (they probably both have addresses, contact information, a contact history, possibly financial transactions, etc).
Larry Lustig
+1  A: 

In addition to Justin's answer: if you make clever use of Foreign Key constraints, you can control what happens when data gets updated or deleted. That way, you can make sure that you do not end up with de-normalized data.

Daan
Never dealt with foreign keys before. Will check it out!
Industrial
+4  A: 

Keep track of a many-to-many relationship in a table specifically for that relationship, turning the relationship into two one-to-many relationships pointing in opposite directions.

CREATE TABLE customer (
    customer_id VARCHAR NOT NULL,
    name VARCHAR NOT NULL,
    PRIMARY KEY (customer_id));

CREATE TABLE publication (
    issn VARCHAR NOT NULL,
    name VARCHAR NOT NULL,
    PRIMARY KEY (issn));

-- Many-to-many relationship for subscriptions.
CREATE TABLE subscription (
    customer_id VARCHAR NOT NULL,
        FOREIGN KEY customer_id REFERENCES customer (customer_id),
    issn VARCHAR NOT NULL,
        FOREIGN KEY issn REFERENCES publication (issn),
    begin TIMESTAMP NOT NULL,
    PRIMARY KEY (customer_id, issn));

You then make use of the relationship table to join other tables through it via the foreign keys.

-- Which customers subscribe to publications named 'Your Garden Gnome'?
SELECT customer.*
FROM customer
    JOIN subscription
        ON subscription.customer_id = customer.customer_id
    JOIN publication
        ON subscription.issn = publication.issn
WHERE
    publication.name = 'Your Garden Gnome';

-- Which publications do customers named 'Fred Nurk' subscribe to?
SELECT publication.*
FROM publication
    JOIN subscription
        ON subscription.issn = publication.issn
    JOIN customer
        ON subscription.customer_id = customer.customer_id
WHERE
    customer.name = 'Fred Nurk';
bignose
Thanks for showing a great practical approach to usage with foreign keys!
Industrial
Can I use Foreign Keys for many-to-many relationships?
Industrial