良好的数据库表设计:一个混合不同实体的表还是为每个实体单独创建表?

9
什么是更好的数据库设计?
方式一:使用一个大表存储不同类型的记录,例如员工、汽车、手机等,为了识别每种类型的记录,我们添加一个名为 "type" 的列。
因此,这个表看起来会有如下列:

id | type | name

1 | car | Ford

2 | car | Toyota

3 | phone | motorola

4 | employee | Jack

5 | employee | Aneesh

6 | phone | Nokia

7 | phone | Motorola

方式二:针对不同类型的记录拆分成不同的表
例如:

Employees

id | name

Cars

id | name

Phones

id | name

这些表可以从其他表中引用外键。如果每个表都有不同的列,则选择方式一通常不可行(除非所有不共同的列都为空)。但如果这些不同实体具有相似的列,那么哪种设计更好?
对于以上两种方案各有利弊,需要根据具体情况进行选择。

你使用哪个关系型数据库? - simeonwillbanks
Oracle。不过这会有影响吗? - Aneesh
枚举类型可以考虑使用,但并非所有关系型数据库原生支持该类型。 - simeonwillbanks
这个回答解决了你的问题吗?你如何在数据库中表示继承? - undefined
4个回答

9
我同意大家的看法——一定要使用单独的表格。使用单独的表格不会使您失去任何东西——只是因为您有更多的表格,您的数据库不会变得更慢或不可管理。
但是您会获得很多好处——您不必为一种实体类型创建许多没有意义的字段等等。正如许多人指出的那样,这样做可以遵循第二范式,这绝对是件好事!
查看 Simple Talk 上这篇有趣的文章:Five Simple Database Design Errors and how to avoid them 错误#1是作者所称的“公共查找表”,听起来非常像您正在尝试做的事情——但是针对真实的生活数据。
阅读这篇文章,内化其所有要求——绝对是优秀的内容,强烈推荐!

5

由于它们是完全不同的类型,我建议将它们存储在不同的表中。这样可以避免维护类型列表,因为它们都在自己的表中。此外,如果将来这些类型之一被扩展(例如,您将为员工存储电话号码),则不会出现像汽车电话号码这样奇怪的关系。这还使得您的数据库更易于理解和维护。


4
Roald van Doorn是完全正确的。如果您有一个单独的表,并以任何方式扩展它,那么您将违反第二正则化形式。正如William Kent所说,“当非键字段是密钥子集的事实时,第二规范形式被违反。” Roald van Doorn关于“员工电话号码”的例子说明了这种违规行为。
当您问自己数据库设计问题时,William Kent的《关系数据库理论中五个标准形式的简单指南》是一篇很好的文章可供参考。

2
我要说,以上所有答案在问题中提到的示例和几乎所有情况下都是正确的。
偶尔我会遇到单个表更好的情况。这种情况很少见,当它出现时,我会想知道是否需要围绕一个单体(中立)实体进行架构。我很快就放弃了这个想法——也许问问别人,但我没有充分陈述我的观点,我们只能回到一直以来的做法。
然后,事实证明,我太晚才意识到我应该制作一个中立实体类型的单个表。
以下是一个支持我的例子:
假设有两种实体类型:公司和人。一家公司通常由一个人拥有,但有时另一家公司拥有一家公司。
保持这个想法,并进一步补充,假设每个公司都有一个注册代理人负责创建公司。并且,进一步说明,注册代理人可以是一个人或另一家公司。
考虑到公司/子公司的所有者/父母可能是一个人或一家公司,您可能开始看到挑战所在。相比之下,如果只有人可以拥有公司,则您的所有权链接表非常传统,具有列:OwnershipID(有点不必要),CorporationID,PersonID。
相反,您需要像这样的内容:OwnershipID,CorporationID,OwnerID,OwnerType。不管怎样,您都可以使其正常工作,但至少不会很有趣。
继续我的例子,您需要为每个公司分配代理人。通常,代理人是所有者之一(一个人)。在这种情况下,您确实希望链接回该人的一个记录。您不希望将该人的记录作为所有者再次作为代理人(在代理表中)。那将是多余的。坏事会发生。 :-)
类似于“问题”,注册代理人也可以是公司,例如法律公司,会计师事务所或Biz Filings公司等典型示例。就像代理人-人一样,代理人-公司也不应该得到自己的记录。它需要链接回已经存在的公司表中的其企业实体的记录。[除了我最终说要有一个公司表]
就像匹配每个公司与其任何类型的所有者(人或公司)的链接表一样,您可以拥有一个代理链接表:AgentRepresentationID,CorporationID,AgentID,AgentType...但是,当您必须汇总相关代理时,从Person表中获取一些代理和从Corporation表中获取一些代理时,它会很丑陋(在我看来)。
因此,在这种情况下,您可以看到中立实体类型的优势。它将是这样的:

表格:EntityAll 关键列: EntityID, EntityType(或EntityTypeID,如果您坚持的话,请链接以获取说明), EntityName(名称和不同类型存在问题...超出本帖范围)

链接表:CorporationOwnership 关键列: OwnershipID(再次强调这有点不必要), ChildEntityID(被拥有的实体;为了清晰起见命名为“Child”,我不会这样命名), ParentEntityID(父实体)

链接表:AgentRepresentation 关键列: AgentRepresentationID(我不会说它), CorporationEntityID(代表公司实体), AgentEntityID(来自实体表,相当于代理记录的实体)

虽然您可能对我的架构感到满意,但是您应该对链接表中的列命名感到有些困扰。它让我感到困扰。通常,这些表中的第二个和第三个列名与您在每个实体的相应表中JOIN的列名完全匹配(哈哈,但是每个实体都没有相应的表,因此您不能使链接表列名与源列名匹配,因为它们是相同的列)。从技术上讲,这并不重要,但它会破坏您的命名约定,这应该很重要,但不足以不这样做。

如果我还没有充分说明,这是如何将其整合在一起的。您可以在EntityAll表格上JOIN自身以获取所需内容。

列出所有公司及其所有者(在T-SQL中):

SELECT Corp.EntityName as CorpName, Owner.EntityName as OwnerName
FROM EntityAll as Corp
JOIN CorporationOwnership as Link on (Corp.EntityID = Link.ChildEntityID)
JOIN EntityAll as Owner on (Link.ParentEntityID = Owner.EntityID)

因此,您需要执行相同的操作来获取代理而不是所有者。我知道这不是我们接受过的架构方法,但我非常有信心,我的解决方案消除了冗余数据,并使编码、管理和阅读更加容易。如果您坚持认为我错了,请告诉我。请建议如何使用单独的公司和个人实体表架构我的示例。干杯!

1
我必须承认,我选择的示例已经使用每个实体类型的表格(甚至代理表)进行了实现。我的需求超出了基本的JOIN逻辑。很快我需要能够在实体类型之间汇总实体(例如公司John Smith CPA,P.A.及其所有者John Smith可以为各种目的汇总到一个逻辑单元)。汇总甚至可能会带来超出链接表已定义的链接范围之外的记录。 - Chris Adragna
我会说,词语“either”表示一个新的实体,我们可以称之为“法人”。它是一个独立的实体,因此应该有自己的表格。它确实有一个属性“underlying”,用于指示它是真实的个人还是公司。我认为这样能使一切更清晰明了。 - Fifnmar

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