使用继承设计数据库是否是一个好主意?

7
例如,我有两张表:'customer'和'staff'。它们几乎相同,只有两个属性不同。那么我应该创建另一个名为'person'的表,包含'customer'和'staff'的所有相同属性,然后创建指向此'person'的外键?这类似于类设计中的继承。
这种方法有什么缺点吗?
5个回答

8

是的,这种方法有一个缺点。连接操作会增加查询复杂度(在某些情况下非常大),如果不小心还会增加查询时间。

相反,标准做法是使用单表继承来模拟对象继承,当子类之间仅有少数属性不同时。这种方法可以避免数据库连接,但会浪费一点未使用的数据库空间。

具体实现方法如下:创建一个包含所有属性的表,包括只适用于其中一个子类的属性,以及一个type属性来指定对象类型。例如,如果customer有以下属性:

idnameemailpasswordorder_date

staff有以下属性:

idnameemailpasswordhire_date

那么就需要创建一个包含所有属性和类型的表:

idtypenameemailpasswordorder_datehire_date

type列始终包含“customer”或“staff”。如果type为“customer”,则hire_date始终为NULL,并且没有意义。如果type为“staff”,则order_date始终为NULL,并且没有意义。


谢谢,如果我有其他表也有id,name,email,password但具有一些不同的特定属性呢? - JatSing
@Sun,然后只需将这些特定属性添加到大列表中。例如,如果您有一个具有id、名称、电子邮件、密码和唯一字段permissions的“administrator”类型,则您的单表将包含id、type、name、email、password、order_date、hire_date、permissions。现在type可以是“customer”、“staff”或“administrator”。不适用于特定类型的字段仅保留为空。明白吗? - Ben Lee
我明白了。但是如果我们像那样连接多个表,你认为一行中会有很多字段保持为空吗? - JatSing
@Sun,这是你根据你的代码需要做出的决定。NULL行很便宜,LEFT JOIN也很便宜。你必须决定是选择JOIN复杂性还是不扩散任何NULL列。关于是否应该使用我的Single Table Inheritance(如我所描述)或者你描述的Class Table Inheritance,没有硬性规定;只有一个经验法则,即如果只有少数字段是唯一的,那么STI通常是更好的解决方案。 - Ben Lee
我们一半的员工也是顾客。我相信对于大多数企业来说都是如此。(但并非所有企业都是这样。) - Mike Sherrill 'Cat Recall'
保持相同的列名,但在业务逻辑中具有不同的含义,这个想法怎么样? 例如 id、type、name、email、password、TypeSpecificDate。在业务部分处理逻辑 如果(Type=Customer) order_date = TypeSpecificDate 否则(Type=Staff) hire_date = TypeSpecificDate我知道这对于查看您的数据库的人来说是不好的做法,您必须保持公共约束或没有约束,但可以减小表格大小。如果您有多种类型可能共享相同类型,则可以进行灵活选择。我有19种不同的类型,所以这是一个解决方案。 - Anestis Kivranoglou

6
你正在描述一个称为类表继承的模式调用。这是一种有效的设计,但像任何其他设计一样,必须慎重使用。阅读马丁·福勒的《企业应用架构模式》以了解其优缺点的更多详细信息。
有些人警告不要使用连接,但只有在需要子类特定列时才需要连接。当给定查询仅需要公共列时,可以避免额外的连接。

4

Pranay Rana和Ben Lee都是正确的,最终答案是:"取决于情况"。

你必须权衡子类专用列的数量与通用列的数量,以决定哪种方式适合你。单表继承不具备良好的可扩展性:如果您必须引入第三种子类(例如供应商),会发生什么情况?

同样,您将如何对待既是员工又是客户的人员?


4
查找"泛化特化关系建模",您会在相关主题上找到一些很好的文章。大多数示例遵循与Bill给您的Class Table Inheritance链接相同的模式。
还有一个小细节。在您的情况下,专用表(针对客户和员工)不会自动编号其id字段。相反,在填充它们时,id字段应该获得广义表(在您的情况下为person)中的id字段的副本。
这使得专用ID发挥双重作用。它们既是pk,也是fk引用相应的genralized表中的行。这使得联接更加容易和快速。
创建具有每个专用表与通用表连接的视图可能很方便。或者您可以创建一个大型视图,生成与其他响应建议的单表继承模式中看到的相同数据。基本上是一堆连接的联合。

当然,有两个ID,一个用于专门的表格,另一个用于通用表格的外键。 - JatSing
3
不,只有一个ID值。沃尔特·米提(Walter Mitty)想表达的是(并且这是一个很好的观点),它们应该是相同的值,从而断言一种“可能具有”的关系,而不是“具有许多”的关系。超类具有自动编号PK,子类共享该PK。 - RET

2
我认为这是好的设计,因为您没有重复数据,这也是数据规范化存在的原因之一。
不过需要注意的一点是,规范化程度越高,需要进行的连接操作就会相应增多。

2
我不同意这个评估。是的,您没有重复数据,但正如您自己指出的那样,您正在扩散“连接”。如果只有少数属性不同,则“单表继承”是一个更好的解决方案,因为它避免了“连接”,同时也不会重复任何数据。 - Ben Lee
@Ben Lee - 这要看您如何看待它,以及数据规模大小,但我肯定会选择 OP 的设计。您提出的设计也不错,但这取决于我们所处的情况...谢谢 - Pranay Rana
你说得对,这取决于具体情况。但是假设只有几行唯一的数据,我认为STI更好。我承认这可能只是我的个人偏好渗入了分析中。 - Ben Lee
1
单表继承的一个可能的缺点是:在所提出的示例中,如果其中一名员工从公司购买任何物品,他将成为客户...假设您在大表继承中有一个“类型”字段,那么您要使用哪种类型?员工?客户?员工客户?当实体不属于多个“类”时,BTI的效果最佳。否则,您会开始在各个地方放置标志和其他东西,例如select * from BigTable where type="STAFF"变得繁琐.... - p.marino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接