Extra tables or non-specific foreign keys?
-
09-06-2019 - |
Question
There are several types of objects in a system, and each has it's own table in the database. A user should be able to comment on any of them. How would you design the comments table(s)? I can think of a few options:
- One comments table, with a FK column for each object type (ObjectAID, ObjectBID, etc)
- Several comments tables, one for each object type (ObjectAComments, ObjectBComments, etc)
- One generic FK (ParentObjectID) with another column to indicate the type ("ObjectA")
Which would you choose? Is there a better method I'm not thinking of?
Solution
@palmsey
Pretty much, but the variation on that pattern that I've seen most often gets rid of ObjectAID
et al. ParentID
becomes both the PK and the FK to Parents
. That gets you something like:
Parents
ParentID
ObjectA
ParentID
(FK and PK)ColumnFromA NOT NULL
ObjectB
ParentID
(FK and PK)ColumnFromB NOT NULL
Comments
would remain the same. Then you just need to constrain ID generation so that you don't accidentally wind up with an ObjectA
row and an ObjectB
row that both point to the same Parents
row; the easiest way to do that is to use the same sequence (or whatever) that you're using for Parents
for ObjectA
and ObjectB
.
You also see a lot of schemas with something like:
Parents
ID
SubclassDiscriminator
ColumnFromA (nullable)
ColumnFromB (nullable)
and Comments
would remain unchanged. But now you can't enforce all of your business constraints (the subclasses' properties are all nullable) without writing triggers or doing it at a different layer.
OTHER TIPS
Is it feasible to design the schema so that the commentable (for lack of a better word) tables follow one of the standard inheritance-modeling patterns? If so, you can have the comment table's FK point to the common parent table.
@Hank Gay
So something like:
- ObjectA
- ObjectAID
- ParentID
- ObjectB
- ObjectBID
- ParentID
- Comments
- CommentID
- ParentID
- Parents
- ParentID
Be careful with generic foreign keys that don't point to exactly one table. Query performance suffers dramatically if you have to split the where condition on a type and point to several different tables. If you only have a few types, and the number of types will not grow, it's Ok to have separate nullable foreign keys to the different tables, but if you will have more types, it's better to come up with a different data model (like @palmsey's suggestion).
One of the things I like to do is have a separate tables that link the generic/common table to all of the individualized tables.
So, for objects Foo and Bar and then comments on Foo & Bar, you'd have something like this:
- Foo
- Foo ID (PK)
- Bar
- Bar ID (PK)
- Comment
- Comment ID (PK)
- Comment Text
- FooComment
- Foo ID (PK FK)
- Comment ID (PK FK)
- BarComment
- Bar ID (PK FK)
- Comment ID (PK FK)
This structure:
- Lets you have a common Comments table
- Doesn't require a DB with table inheritance
- Doesn't pollute the Foo and Bar tables with Comment-related information
- Lets you attach a Comment to multiple objects (which can be desireable)
- Lets you attach other properties to the junction of Foo/Bar and Comment if so desired.
- Still preserves the relations with standard (ie: fast, simple, reliable) foreign keys