支持仅适用于特定行的值的最佳数据库模式是什么?

时间:2011-12-31 01:23:30

标签: database-design database-schema

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

  1. Id(PK)
  2. 名称
  3. 描述
  4. CalendarTypeId(FK到 CalendarType 表格)
  5. 我有一个名为 CalendarType 的表格,其中包含字段

    1. Id(PK)
    2. 姓名
    3. 描述
    4. 问题是我需要为日历类型为2的每个日历存储一个附加字段。(但此字段与任何其他日历类型无关)。

      我应该只在日历表中创建一个新字段,并忽略具有不同calendarTypeid的所有其他日历的该字段,或者是否有更好的方法来组织此模式以支持此需求。< / p>

5 个答案:

答案 0 :(得分:15)

好的,这是你现在拥有的ER模型(省略基数):

现在,让我们关注Calendar和SubCalendar。显然,你有一个层次结构。但层次结构如何变成表格?有三种常见的方法可以做到这一点:

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

优点:没有空值,因为每个表都将拥有它所需的全部内容。也不需要加入。如果您将运行查询仅搜索一种类型的子项,则此架构将非常有用,因为您不需要按类型进行筛选,因为每个表只存储一种类型

缺点:此架构不适用于您有重叠子项的情况。换句话说,如果父行在向每个子节点发送字段时可以有多个子节点,则父节点数据将在每个子节点中重复。不好,所以如果是这样的话,不要使用这种策略。另外,如果你有很多孩子,每个孩子的记录很少,你就会有很多桌子,每张桌子的记录都很少,所以管理起来可能会有点困难

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

优点:所有孩子只有一张桌子,因此易于管理。不需要加入。如果针对此表运行的大多数查询需要来自多个类型的子项的结果,则可能会很有用。

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

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

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

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

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

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

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

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

答案 1 :(得分:5)

我可能会使用日历。我称之为重载Db表。当数据存储昂贵时,这是一种犯罪。现在它被称为解决问题的简单方法并继续前进。永远不要过度工程,直到你真的需要。

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

答案 2 :(得分:4)

也许我只是简单地看着这个,但是如果你坚持使用“在重用之前使用”的模型,那么正确的做法就是将无效列添加到日历表中并添加一个检查约束到日历类型,确保日历类型= 2时不为空。

这是直截了当的,而且最重要的是它很容易测试。

对于这个答案,我可能会有些松懈(可能不是最有效的),但这完全取决于解决方案的规模。现实情况是,在接下来的几个月里,这些约束可能会发生很大变化,当你不知道那是什么时,你不想通过选择“正确”的方式将自己描绘成一个角落。完全可能的是,当你进入第10个日历类型时,会出现一个模式,它会告诉你最好(或最正常)的方式。现在,只需保持简单,便于测试,以后便于更改。

答案 3 :(得分:2)

您可以使用接近建议的单表继承模式

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

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

如果你想专门化一些表来匹配你试图在数据库中表示的类型(Calendar和CalendarType2)

答案 4 :(得分:2)

Leora,

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

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

斯蒂芬