Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I resolve a three-way polymorphic association?

First let me say I am using MySQL (not transactional) and that this can't be changed. Also, I have simplified the tables here for brevity and clarity.

In this example a 'Lesson' is comprised of it's internal attributes and an external attributes with it's own attributes 'Readings'. 'Readings' has it's own key dependent attributes and three distinct external attributes (the reading sources).

I want to avoid the polymorphic association that arrises here but am unable to wrap my head around it. In this example 'sourceId' in table 'Reading' would contain the id from one of three tables "ExternalURL", "InternalURL" and "Book". Also, the field "polytable" would contain the table name that the aforementioned "id" came from.

Can someone take a moment to please explain how to resolve this maintaining RI or is it possible that in the interest of efficiency that it should be left?

Thank you for your time and consideration,

Tim

                       -------------
                      | ExternalURL |
                       -------------
                      | id          |
                      | badlink     |
                      | url         |
                      |             |
                      |             |
                       -------------
                            |
                            |
                            |
                           / \
 ------------          -------------          -------------
| Lesson     |-------<| Reading     |>-------| InternalURL |
 ------------          -------------          -------------
| id         |        | id          |        | id          |
| label      |        | lessonId    |        | url         |
| summary    |        | sourceId    |        |             |
| lessonOrder|        | polytable   |        |             |
| active     |        | label       |        |             |
 ------------          -------------          -------------
                            \ /
                             |
                             |
                       ------------  
                      | Book       |
                       ------------ 
                      | id         |
                      | label      |
                      | summary    |
                      | lessonOrder|
                      | active     |
                       ------------ 
like image 971
STEAMworks Learning Center Avatar asked Mar 05 '13 00:03

STEAMworks Learning Center


People also ask

Why we use polymorphic association in Rails?

In Ruby on Rails, a polymorphic association is an Active Record association that can connect a model to multiple other models. For example, we can use a single association to connect the Review model with the Event and Restaurant models, allowing us to connect a review with either an event or a restaurant.

What is polymorphic in Rails?

Polymorphic relationship in Rails refers to a type of Active Record association. This concept is used to attach a model to another model that can be of a different type by only having to define one association.

What is polymorphic association in SQL?

The indications that the Requirements create a situation where someone might try to use a Polymorphic Association are: There are several entities which are sufficiently similar that you consider having one entity, but are sufficiently different that you think multiple entities are justified.

What is polymorphic association in Sequelize?

Concept​ A polymorphic association consists on two (or more) associations happening with the same foreign key. For example, consider the models Image , Video and Comment . The first two represent something that a user might post. We want to allow comments to be placed in both of them.


1 Answers

You have at least a couple of options to preserve RI:

  1. Add several nullable FK columns in Reading, one for each reading type. Only one of these should be non-null.

    CREATE TABLE Reading (
      id INT AUTO_INCREMENT PRIMARY KEY,
      lessonId INT NOT NULL,
      bookId INT NULL,
      externalUrlId INT NULL,
      internalUrlId INT NULL,
      FOREIGN KEY (bookId) REFERENCES Book(id),
      FOREIGN KEY (externalUrlId) REFERENCES ExternalUrl(id),
      FOREIGN KEY (internalUrlId) REFERENCES InternalUrl(id)
    );
    

    Enforcing that exactly one of the foreign key columns is non-null is a task for a trigger, or else you'd have to do it in application code. But at least you can define the foreign keys.

  2. Add a supertable Readable which is a parent of each of the other specific readable types.

    CREATE TABLE Readable (
      id INT AUTO_INCREMENT PRIMARY KEY,
      readable_type CHAR(1) NOT NULL,
      UNIQUE KEY (id, readable_type)
    );
    
    CREATE TABLE Book (
      id INT PRIMARY KEY, -- not AUTO_INCREMENT
      readable_type CHAR(1) NOT NULL, -- must be 'B'
      FOREIGN KEY (id, readable_type) REFERENCES Readable(id, readable_type)
    );
    
    ... similar tables for ExternalUrl and InternalUrl...
    

    Then make Reading also reference Readable.

    CREATE TABLE Reading (
      id INT AUTO_INCREMENT PRIMARY KEY,
      lessonId INT NOT NULL,
      sourceId INT NOT NULL,
      FOREIGN KEY (sourceId) REFERENCES Readable(id)
    );
    

    I describe this solution in more detail in my answer to Why can you not have a foreign key in a polymorphic association?.

like image 175
Bill Karwin Avatar answered Sep 25 '22 17:09

Bill Karwin