Foreign Key column mapped to multiple primary keys

前端 未结 3 1515
再見小時候
再見小時候 2020-12-06 23:37

I have a database which has three tables

Messages - PK = MessageId
Drafts - PK = DraftId
History - FK = RelatedItemId

The History table has a sin

相关标签:
3条回答
  • 2020-12-06 23:56

    Is there a name for this relationship?

    There is no standard name that I'm aware of, but I've heard people using the term "generic FKs" or even "inner-platform effect".

    Is it just bad design?

    Yes.

    The reason: it prevents you from declaring a FOREIGN KEY, and therefore prevents the DBMS from enforcing referential integrity directly. Therefore you must enforce it trough imperative code, which is surprisingly difficult.

    Is there a better way to design this relationship?

    Yes.

    Create separate FOREIGN KEY for each referenced table. Make them NULL-able, but make sure exactly one of them is non-NULL, through a CHECK constraint.

    Alternatively, take a look at inheritance.

    0 讨论(0)
  • 2020-12-07 00:04

    In a short description the solution you have used is called:
    Polymorphic Association
    Objective: Reference Multiple Parents
    Resulting anti-pattern: Use dual-purpose foreign key, violating first normal form (atomic issue), loosing referential integrity
    Solution: Simplify the Relationship

    More information about the problem.

    BTW createing a common super-table will help you:

    enter image description here

    0 讨论(0)
  • 2020-12-07 00:07

    Best practice I have found is to create a Function that returns whether the passed in value exists in either of your Messages and Drafts PK columns. You can then add a constraint on the column on the History that calls this function and will only insert if it passes (i.e. it exists).

    Adding non-parsed example Code:

    CREATE FUNCTION is_related_there ( IN @value uniqueidentifier ) RETURNS TINYINT BEGIN IF (select count(DraftId) from Drafts where DraftId = @value + select count(MessageId) from Messages where MessageId = @value) > 0 THEN RETURN 1; ELSE RETURN 0; END IF; END;

    ALTER TABLE History ADD CONSTRAINT CK_HistoryExists CHECK (is_related_there (RelatedItemId) = 1)

    Hope that runs and helps lol

    0 讨论(0)
提交回复
热议问题