sqlpostgresqlforeign-keysrelational-databasepolymorphic-associations

Proper way to model M:N relationship in SQL where parent may be one of many types


I have a situation where a particular child object may have multiple parents of different types. For example, a foo object may be a child of one ore more of a, b, or c objects. More over, I also have a bar object which may also be a child of one or more a, b, or c objects. What's the proper way to model this in SQL?

A) Single table for all relationships:

relationship_tbl
parent_id  parent_type  child_id  child_type
---------  -----------  --------  ----------
1          a            5         foo
2          a            6         foo
3          c            7         bar
4          b            7         bar

B) Unique table for each parent type:

a_child_tbl
parent_id  child_id  child_type
---------  --------  ----------
1          5         foo
2          6         foo

b_child_tbl
parent_id  child_id  child_type
---------  --------  ----------
4          7         bar

c_child_tbl
parent_id  child_id  child_type
---------  --------  ----------
3          7         bar

C) Unique table for each child type:

foo_parent_tbl
child_id   parent_id    parent_type
---------  -----------  -----------
5          1            a
6          2            a

bar_parent_tbl
child_id   parent_id    parent_type
---------  -----------  -----------
7         3             c
7         4             b

D) Unique table for each combination

a_foo_tbl
parent_id  child_id
---------  --------
1          5
2          6

b_bar_tbl
parent_id  child_id
---------  --------
4          7

c_bar_tbl
parent_id  child_id
---------  --------
3          7

E) Some other strategy that I haven't explored

To me, it seems like A would be the easiest to query and answer questions like Find all the parents of child 7 or Find all the children of parent 4, but I've read some advice that basically says never create generic tables for parent/child relationships.

Could somebody shed some light on the best way to do this and why? It's safe to assume that the table will never have more than a few million rows in it.


Solution

  • I'd recommend a variation of your solution C. You need to have a separate M:N table for each M:N relationship, per Fourth Normal Form.

    But also create a supertable to unify all the a, b, c parent types, so that the M:N tables can reference a single table, in which each parent_id is strictly assigned its respective type.

    parent_tbl
    parent_id parent_type
    --------- -----------
    1         a
    2         a
    3         c
    4         b
    
    a_parent_tbl
    parent_id parent_type
    --------- -----------
    1         a
    1         a
    
    b_parent_tbl
    parent_id parent_type
    --------- -----------
    4         b
    
    c_parent_tbl
    parent_id parent_type
    --------- -----------
    3         c
    

    In each sub-parent table, the parent_type is restricted to a single value. The parent_table has a unique constraint on (parent_id, parent_type), and the foreign key in each sub-parent tables references the columns in that unique constraint. Therefore no parent_id can be referenced by more than one sub-type.

    Then your child M:N tables only need to reference parent_table by ID. You don't necessarily need a parent_type column in these tables, but you do if you need to create a UNIQUE constraint over (child_id, parent_type) so that each child can have only one parent of a given type.

    foo_parent_tbl
    child_id parent_id
    -------- ---------
    5         1
    6         2
    
    bar_parent_tbl
    child_id parent_id
    -------- ---------
    7        3
    7        4
    

    You may also like to read: