建模以下类型关系的最佳方法是什么?
假设我有代表两种类型实体的表:company
和person
. 我有一个customer
带有entity_id
字段的表,它应该引用 acompany
或 a person
。
如果我只是将 acompany_id
或 a存储person_id
在其中,entity_id
那么我将不知道在哪个表中查找实体。我想到的第一个解决方案是有一个entity_type
字段,customer
其中将值“company”或“person”告诉我我的外键entity_id
指的是哪个表。我不确定这是否是最好的方法。
这似乎应该是数据库设计中相当普遍的问题,所以我希望有一种我可以采用的设计模式。
我在更广泛的意义上在标题中使用了多态这个词,所以我不是指继承。在我的示例中,公司不是人(反之亦然),但它们仍然可以互换。我在 Stack Exchange 网站上发现的其他问题假设继承也是一项要求,所以我不确定是否有合适的副本。
似乎有两种主要方法可以模拟这种情况:
(1) 使用超类型/子类型。但是,由于我们不是在讨论“面向对象”设计,所以第二种选择可能更合适一些。
(2)使用所谓的ARC,表示XOR关系。
假设我们有一个实体 TRANSACTION。每个客户都可以参与一项或多项交易,并且客户是公司或个人(XOR)。我们的 ERD/关系模型可能如下所示。请注意跨越两个关系绘制的“弧”。
至于实现:您可以编写一个 CHECK 约束(以避免触发),这将强制执行 XOR(异或)。完整的 DDL 代码如下所示(注意 CHECK):
(在 Oracle 12c 上测试)
我知道您说您不是指继承,但是关于您的案例的最佳文献都在描述问题和提出解决方案时提到了继承。
有一些网站提供了有关如何设计表达类型/子类型或类/子类关系的关系表的详细解决方案。最重要的是在搜索中使用正确的流行语。
在客户、公司和个人之间的实体关系建模中,称为“泛化/专业化”。对此进行搜索将为您提供很多好的材料。公司是专门的客户,个人也是。另一个有用的流行语是“IS-A 关系”。
在关系建模中,有三种技术被广泛共享用于解决此类问题。这里分别是“单表继承”、“类表继承”、“共享主键”。我知道那里出现了“继承”一词,但相信我,搜索这些项目将为您提供一些针对您的具体案例的文章,尽管它们可能是在谈论汽车、卡车和车辆或狗、猫和宠物公司、个人和客户。
您将获得的一些点击将带您回到这里,访问 stackexchange.dba。其他人会带你到 Stackoverflow,那里有三个标签来收集关于这三种技术的信息。
唯一没有详细介绍的是如何添加约束来强制执行互斥规则,即所谓的 XOR。如果您使用共享主键,则互斥的约束将与您已经介绍的非常相似,只是更简单。
总结一下这一切在您的情况下意味着什么:
您将有四个表:transaction、customer、person 和 company。
Transaction 将包含一个 customer_id 字段,该字段引用 customer 中的 id 字段。客户将有一个 id 字段,可能由自动编号功能填充,就像通常所做的那样。Person 没有 id 字段。相反,它有一个 company_id 字段,它既充当主键又充当引用 company.id 的外键。公司的处理方式与人员相同,company_id 字段既是 PK 也是 FK。
客户表可以包含与公司和个人相关的字段(如果有)。
最后,您需要在 person 表中设置一个约束,表明 customer_id 不在 company 表中,并且在 company 表中设置一个约束,表明 customer_id 不在 person 表中。