【问题标题】:Good db table design: One table mixing different entities or separate table for each entity良好的数据库表设计:一张表混合不同的实体或为每个实体单独的表
【发布时间】:2011-01-22 08:26:11
【问题描述】:

什么是更好的数据库设计?

有一个可以包含不同“类型”记录的大表,例如:员工、汽车、手机,为了识别每种类型的记录,我们有一个名为 type 的列。

所以表格的列看起来像

标识 |类型 |名称

1 |汽车 |福特

2 |汽车 |丰田

3 |电话 |摩托罗拉

4 |员工 |杰克

5 |员工 |阿尼什

6 |电话 |诺基亚

7 |电话 |摩托罗拉

或为每种类型设置不同的表格

例如:

员工

标识 |名字

汽车

标识 |名字

电话

标识 |名字

这些表可能有来自其他表的外键引用。现在,如果每个表都有不同的列,那么决定将很简单,您不能在同一个表中拥有它。所以选项 1 可能被排除(除非所有不常见的列都可以为空)。但是,如果这些不同的实体有相似的列怎么办,在这种情况下有什么更好的设计呢?

支持和反对两者的论据可能是什么?

【问题讨论】:

  • 您使用的是哪个关系数据库?
  • 甲骨文。不过这有关系吗?
  • 可以考虑枚举类型,但并非所有关系数据库本身都支持该类型。

标签: database-design


【解决方案1】:

我同意每个人的观点 - 绝对使用单独的表格。拥有单独的表不会丢失任何东西 - 仅仅因为您有更多的表,您的数据库不会变得更慢或更难管理。

但是你会收获很多——你不必有很多对一种类型的实体没有意义的字段,等等。正如许多人指出的那样,您坚持 2NF,这绝对是一件好事!

查看这篇名为“简单对话”的有趣文章

Five Simple Database Design Errors and how to avoid them

错误 #1 是作者所说的“通用查找表”,这听起来很像您正在尝试做的事情 - 但针对的是真实的实时数据。

阅读这篇文章,内化它的所有要求 - 优秀的东西,强烈推荐!

【讨论】:

    【解决方案2】:

    由于它们是真正不同的类型,我建议将它们存储在单独的表中。这避免了维护类型列表,因为它们都在自己的表中。此外,如果将来扩展其中一种类型(例如,您要存储员工的电话号码),您将不会得到任何奇怪的关系,例如汽车的电话号码。它还使您的数据库更易于理解和维护。

    【讨论】:

      【解决方案3】:

      Roald van Doorn 是绝对正确的。如果您有一个表并以任何方式扩展它,您将违反第二范式。正如威廉肯特所说,“当非关键字段是关于关键子集的事实时,违反了第二范式。” Roald van Doorn 的“员工电话号码”示例说明了这种违规行为。

      William Kent 的 A Simple Guide to Five Normal Forms in Relational Database Theory 是一篇很棒的论文,当你问自己一个数据库设计问题时,可以回顾一下。

      【讨论】:

        【解决方案4】:

        我会说,上面所有的答案都是 1. 考虑到问题中引用的例子是正确的,并且 2. 几乎所有时间都是正确的。

        时不时我都会遇到单张桌子更好的情况。它是如此罕见,以至于当它出现时,我想知道我是否需要围绕一个整体(中性)实体进行架构设计。我很快就打消了这种冲动——也许是问别人,但我没有充分说明我的情况,于是我们又按照我们一贯的方式去做了。

        然后,事实证明,在游戏中太晚了,我发现我应该制作一个中性实体类型的表。

        下面是我的例子:

        假设有两种实体类型,一个公司和一个人。一家公司通常由一个人拥有,但有时另一家公司拥有一家公司。

        坚持这个想法并补充,假设每个公司都有一个注册代理人,负责公司的合法创建。而且,为了进一步说明,注册代理人可以是个人或其他公司。

        鉴于公司/孩子的所有者/父母可以是个人或公司,您可能会开始看到挑战。相比之下,如果只有人可以拥有公司,那么您的 Ownership 链接表非常传统,其中包含以下列:OwnershipID(有点不必要)、CorporationID、PersonID。

        相反,您需要类似:OwnershipID、CorporationID、OwnerID、OwnerType 不知何故,您可以使这项工作发挥作用,但至少可以说不会很有趣。

        继续我给出的示例,您需要为每个公司分配一个代理。通常,代理人是业主之一(一个人)。在这种情况下,您确实希望链接回该人的一条记录。您不希望将该人记录为所有者,然后再次作为代理(在代理表中)。那将是多余的。坏事会发生。 :-)

        与该“问题”类似,注册代理人也可以是公司,例如律师事务所、注册会计师或 Biz Filings 公司,列举了一些典型的例子。就像代理人一样,代理人公司真的不应该有自己的记录。它需要链接回 Corporation 表中已经存在的公司存在记录。 [除了我最终说没有有一个公司表]

        就像将每个公司与其任何类型、个人或公司的所有者匹配的链接表一样,您可以拥有一个代理链接表:AgentRepresentationID、CorporationID、AgentID、AgentType。 .. 但是,当您必须将相关代理聚集在一起时(一些来自 Person 表,一些来自 Corporation 表),这将是丑陋的 (IMO)。

        因此,在这种情况下,您可以看到中性实体类型如何具有优势。应该是这样的:

        表:EntityAll 关键列: 实体 ID, EntityType(或 EntityTypeID,如果您坚持,请链接获取描述), EntityName(名称和不同类型存在问题……与本帖无关)

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

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

        虽然您可能对我的体系结构没意见,但您应该对链接表中的列命名有点困扰。这让我很烦。通常,这些表中的第二和第三列名称与每个实体各自表中的 JOIN 列的名称完全匹配(哈哈,但每个实体没有各自的表,所以你可以' t 使链接表列名与源列名匹配,因为它们是同一列)。从技术上讲,这无关紧要,但它会破坏您应该重要的命名约定,但还不足以不这样做。

        如果我还没有把它开回家,下面是你如何把它拉到一起的方法。你 JOIN EntityAll 表本身来获得你需要的东西。

        列出所有军团及其所有者(在 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)
        

        因此,您会做同样的事情来获取代理,而不是所有者。

        我意识到这不是我们训练架构事物的方式,但我非常强烈地认为我的解决方案消除了冗余数据,并且更容易编码、管理和阅读。

        不过,如果你坚持认为我错了,请告诉我。建议您如何使用单独的 Corporation 和 Person 实体表来构建我的示例。干杯!

        【讨论】:

        • 我应该承认,我选择的示例是我已经通过使用每种实体类型的表(甚至是代理表)实现的示例。我的需求超出了基本的 JOIN 逻辑。很快我就需要能够跨实体类型汇总实体(例如 John Smith CPA, P.A. 公司及其所有者,John Smith 可以汇总到用于各种目的的逻辑单元)。汇总甚至可能会在链接表定义的现有链接之外引入记录。
        猜你喜欢
        • 2013-02-22
        • 2015-05-08
        • 1970-01-01
        • 1970-01-01
        • 2011-02-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        相关资源
        最近更新 更多