我有一个有三个表的数据库
消息 - PK = MessageId
草稿 - PK = DraftId
历史 - FK = RelatedItemId
历史记录表只有一个外键[RelatedItemId]
,可以映射到Messages
和Drafts
中的两个主键之一。
这段关系有名字吗?
这只是糟糕的设计吗?
有没有更好的方法来设计这种关系?
以下是此问题的CREATE TABLE语句:
CREATE TABLE [dbo].[History](
[HistoryId] [uniqueidentifier] NOT NULL,
[RelatedItemId] [uniqueidentifier] NULL,
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED ( [HistoryId] ASC )
)
CREATE TABLE [dbo].[Messages](
[MessageId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED ( [MessageId] ASC )
)
CREATE TABLE [dbo].[Drafts](
[DraftId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Drafts] PRIMARY KEY CLUSTERED ( [DraftId] ASC )
)
答案 0 :(得分:4)
在简短描述中,您使用的解决方案称为:
多态协会
目标:参考多个父母
产生的反模式:使用两用外键,违反第一范式(原子问题),失去参照完整性
解决方案:简化关系
More information about the problem
BTW创建一个共同的超级表将帮助你:
答案 1 :(得分:2)
这段关系有名字吗?
我没有注意到标准名称,但我听说有人使用“通用FK”这个术语甚至“inner-platform effect"。
这只是糟糕的设计吗?
是
原因:它阻止您声明FOREIGN KEY,因此阻止DBMS直接强制引用完整性。因此,您必须通过命令式代码强制执行此操作,即surprisingly difficult。
有没有更好的方法来设计这种关系?
是
为每个引用的表创建单独的FOREIGN KEY。使它们成为NULL,但通过CHECK约束确保它们中的一个非NULL。
或者,请查看inheritance。
答案 2 :(得分:0)
我发现的最佳实践是创建一个Function,它返回传入的值是否存在于Messages和Drafts PK列中。然后,您可以在调用此函数的历史记录上的列上添加约束,并且只有在它通过时才会插入(即它存在)。
添加未解析的示例代码:
CREATE FUNCTION is_related_there( IN @value uniqueidentifier) 返回TINYINT 开始 IF(从草稿中选择计数(DraftId),其中DraftId = @value + select Messages(MessageId)from Messages where MessageId = @value)> 0那么 返回1; 其他 返回0; 万一; END;
ALTER TABLE历史记录添加约束 CK_HistoryExists CHECK(is_related_there(RelatedItemId)= 1)
希望运行并帮助lol