【问题标题】:What is the best database schema to support values that are only appropriate to specific rows?支持仅适用于特定行的值的最佳数据库模式是什么?
【发布时间】:2012-01-30 22:04:35
【问题描述】:

我有一个名为 Calendar 的数据库表,其中包含字段

  1. 标识(PK)
  2. 姓名
  3. 说明
  4. CalendarTypeId(FK 到 CalendarType 表)

我有另一个名为 CalendarType 的表,其中包含字段

  1. 标识(PK)
  2. 姓名
  3. 说明

问题是我需要为日历类型为 2 的每个日历存储一个附加字段。(但此字段与任何其他日历类型无关)。

我是否应该在 Calendar 表中创建一个新字段并忽略该字段以用于具有不同 calendarTypeid 的所有其他日历,或者是否有更好的方法来组织此架构以支持此需求。

【问题讨论】:

  • 理论上会告诉您需要一个单独的表,其中包含日历的 ID 和新字段。实践表明,在大多数情况下,这将是多余的,除非您预计随着时间的推移需要额外的此类列。

标签: database-design database-schema


【解决方案1】:

好的,这是您当前拥有的 ER 模型(省略基数):

现在,让我们关注日历和子日历。显然,你在那里有一个层次结构。但是层次结构是如何变成表格的呢?有三种常见的方法来做到这一点:

1) 杀死父实体并保留子实体:在这种情况下,您删除父实体并将该实体的所有字段发送给每个子实体。在您的示例中,您只有一个孩子,因此所有父母的属性都只属于它。

优点:没有空值,因为每个表都会有它需要的一切。也不需要连接。如果您将运行仅搜索一种类型的子项的查询,则此模式将很有用,因为您不需要按类型进行过滤,因为每个表将仅存储一种类型

缺点:此架构不适合您有重叠孩子的情况。换句话说,如果在将字段发送给每个子项时父行可以有多个子项,则父数据将在每个子项中重复。不好,所以如果是这种情况,请不要使用此策略。此外,如果您有很多子表并且每个表中的记录很少,那么您将有很多表,每个表中的记录很少,因此管理起来可能会有点困难

2) 杀死孩子并保留父母:在这种情况下,您删除所有孩子并将其所有属性发送给父母。由于父级现在是其自身及其所有子级的混合体,因此需要一种方法来确定哪一行属于哪种类型的子级。这是通过向父实体添加一个新属性来完成的,该属性将确定每一行的类型(无论数据类型如何)。

优点:所有孩子只有一张桌子,所以很容易管理。不需要连接。如果针对此表运行的大多数查询需要来自不止一种类型的子级的结果,这可能会很有用。

缺点:同样,如果父级可以拥有与多个子级相关的行,则数据将被复制,因为每个子级将有一行,因此在此有一个限制解决方案。此外,必须添加一个新列作为元数据。表中的记录量会更大。必须将 Null 值分配给子级拥有的数据以及父级或其他子级拥有的数据。

3) 全部保留:最不血腥的解决方案是不杀死任何东西 :) 在这种情况下,层次结构被父级和每个子级之间的关系所取代。这样,孩子必须通过外键连接到父表才能访问父表的数据。

优点:没有数据重复,也没有空值。每个实体只有最少量的数据,其余的可以通过加入父表来获得。在这种情况下,父行可以链接到多个子行,而无需复制数据。如果将运行许多查询,而这些查询只能满足一个表(通常是父表),那么这是一个不错的选择。还有一点是很容易扩展到更多的日历,例如,如果要添加一个需要新字段的新日历,则必须添加一个新表,而不需要修改当前的表

缺点:需要最多的表(实际上比第一个多一个)。每个孩子都需要一个连接,这将降低数据集越大的性能。此外,需要外键来连接两个表。如果大多数查询需要来自父级和子级的数据,则此架构在性能方面将是最差的

现在,您问的是 best 数据库架构。我认为现在很清楚,这取决于要求、将要运行的查询类型、数据的结构方式等。

但是,我可以对此进行更多分析。您说您有一个日历表,有时其中一个需要更多数据。所以我们可以说我们有两种类型的日历,父类和子类。所以我们可能认为选择解决方案 2 是一个很好的可能性,因为您将有 2 行代表每种类型,但我们错了。这是因为在这种情况下,每个孩子都包括其父母。现在,如果我们可以假设如果 SubAttribute 对于孩子始终为非 null 而对于父母始终为 null,我们甚至可以删除 CalendarType,这实际上将导致解决方案 1。

最后,根据经验(主要是因为大多数查询在现实生活中都有很多连接),如果您想关注性能,您应该选择解决方案 1,否则,如果您想专注于规范化设计,您应该选择解决方案 3。

我希望这已经消除了一些疑虑并可能产生了其他疑虑:)

【讨论】:

  • 解决方案1的另一个问题是一些数据库没有序列生成,因此当这个实体要在另一个实体中引用时会出现问题。
  • 嗯。您分析了 Q 并以“解决方案 1,否则解决方案 3”结束。我的分析是“解决方案 2,否则解决方案 3”。您对解决方案 1 的分析有误。 “没有空值,因为每个表都会有它需要的一切”。不,日历类型 1 的字段为空。或者你有两个不同的表,用于两个不同的日历(我相信你会同意这是一个非首发)。也许您无意中将解决方案 1 变为 解决方案 2。对于解决方案 2,已经有一个 IsOfType 字段,因此需要添加元数据也是一个错误。解决方案 2 只是“将列向上移动到父级”。
  • @ToolmakerSteve 我从来没有说过“解决方案 1,否则解决方案 3”。我明确表示这取决于 OP 需要关注什么。请仔细阅读结论段落。然后你说“不,日历类型 1 的字段为空。否则你有两个不同的表”。 杀死父母并保留孩子方法是解决层次结构的经典解决方案。许多有关数据建模的书籍对此进行了更详细的解释。大概,我没能总结出来。如果您阅读其中任何一个,您会意识到您确实需要多个表格,这是一个完全有效的解决方案
  • @ToolmakerSteve 关于你提到的第二个错误,正如我在回答中所说,我只是关注日历和子日历整个分析。 OP 可以使用我在方法 2 中提到的 CalendarType 字段作为原始模型中的 CalendarTypeId FK,因为他们可能更喜欢添加不同的拆分方式模型
  • 很公平。我意识到您提供的答案可能对未来的多个读者有用,他们的要求可能略有不同。您向读者展示如何进行良好的分析,真是太好了。我正在对您分析的复杂性做出反应,这似乎使您得出一个结论,我仍然认为,该结论降低了专门针对 OP 可以说是最简单的解决方案。
【解决方案2】:

我可能会使用日历。我称之为重载 Db 表。当数据存储成本高昂时,这是一种犯罪行为。现在它被称为以简单的方式解决问题并继续前进。除非你真的需要,否则永远不要过度设计。

但是,您没有明确说明对于 typeID 为 2 的每个 Calendar 实例的额外字段值是否不同。有时我的 Type 表有子类型字段等,但我会假设是 Calendar 实例的情况类型 2 将在必填字段中具有不同的值。

【讨论】:

    【解决方案3】:

    也许我看得太简单了,但是如果您坚持“重用前使用”的模型,那么正确的做法是简单地将可为空的列添加到您的日历表并添加一个检查约束到日历类型,如果日历类型 = 2,则确保它不为空。

    它很简单,最重要的是它很容易测试。

    我可能会对此答案有些懈怠(可能不是最有效的),但这完全取决于您的解决方案的规模。现实情况是,这些限制可能会在接下来的几个月内发生很大变化,当您还不知道那是什么时,您不想通过选择“正确”的方式将自己逼入绝境。当您到达第 10 种日历类型时,完全有可能会出现一种模式,它确实会告诉您最好(或最正常)的方法。目前,只需保持简单,使其易于测试和日后易于更改。

    【讨论】:

      【解决方案4】:

      你可以使用单表继承模式,和你的建议很接近,

      http://martinfowler.com/eaaCatalog/singleTableInheritance.html

      http://martinfowler.com/eaaCatalog/classTableInheritance.html

      如果您想专门化某些表以匹配您尝试在数据库中表示的类型(Calendar 和 CalendarType2)

      【讨论】:

      • 比。 .老实说,我对这些链接的问题是,它们并没有真正探讨不同设计的优缺点(除非我遗漏了什么),它们只是定义了一种方法
      【解决方案5】:

      莱奥拉,

      我建议您使用日历表,并将其他日历类型不需要的额外字段设为空。随着需求的变化,您可以通过这种方式向日历表中添加更多属性。

      我还建议为您的模型创建一个基本日历类,然后创建使用 calendartypeid 字段映射的子类,并根据需要在您的应用程序中使用特定的日历子类。大多数 ORMS 将支持这种类型的映射,如果需要,还允许您渲染每个子类与其他子类不同

      斯蒂芬

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多