数据库设计:第一范式和重复组

时间:2009-06-04 15:28:25

标签: database-design normalization

要坚持第一范式,您必须避免的一件事是重复组。而不是:

    CustID  Name  Address       Phone1      Phone2       Phone3

     102    Jerry  234 East..   555-2342   555-9854     555-2986

您应该创建第二个电话号码表,然后在连接上创建:

CustID  Name     Address       Phone

102 Jerry    234 East..   555-2342
102 Jerry    234 East..   555-9854
102 Jerry    234 East..   555-2986

有时,它有点模棱两可,而且很难判断一组列标题何时合格。例如,假设您目前在每个硬件上运行两次测试。您的第一个数据库设计产生了最横向的方法:

设计1

SN     Test1_Max   Test1_Min    Test1_Mean  Test2_Max   Test2_Min    Test2_Mean
2093      23          2            15         54          -24           45  

显然,这是一个重复组,可以更容易地表示为(在“部件”和“测试”之间的连接上):

设计2

SN     Test      Max    Min    Mean     
2093    1        23     2      15       
2093    2        54     -24     45      

但是,你可以更加垂直:

设计3

SN     Test    Statistic    Value
2093    1        Max          23
2093    1        Min          2
2093    1        Mean         15       
2093    2        Max          54
2093    2        Min         -24
2093    2        Mean         45  

设计3是否必要?你如何决定它的垂直度?设计2和3之间的优缺点是什么?看起来两者都可以通过SQL轻松选择或加入,具有设计3的优势,因为您可以轻松添加新的统计数据而无需实际修改表结构。

但是在任何人去之前说越垂直越好,有时会更模糊。像:

设计4

SN      AverageCurrent (mA)    BatteryCapacity (mA)  
2093          200                    540  

可以改为:

设计5

SN      mA_Measuremnt       Value
2093    AverageCurrent      200 
2093    BatteryCapacity     540 

虽然两个属性属于同一个域(mA),但它们代表了与组件相关的非常不同的东西。在这种情况下,Design 4更好,因为它不是严格意义上的重复组吗?我想我正在寻找的是一些标准,知道何时将其分解为更多的表格,从而使其更加垂直。

总结这个非常长的问题,如果它们只是相同的域具有完全相同的含义,你应该只删除并规范化重复组吗?。如果是这种情况,那么实际上只有电话示例和设计1中的两个测试可能符合此标准。虽然看起来设计3和5可能有设计上的好处,但即使设计3的统计数据严格地说有不同的含义,而且平均电流和电池容量在设计5中肯定有不同的含义。

8 个答案:

答案 0 :(得分:7)

设计2和设计4是最好的方法,只要结果不会总是存在(在Desigin 1中也称为NULL)。如果他们总是被采取,那么第一个设计是好的。

我相信在SQL中重复的​​组实际上是你有一个填充了add'l值的列,例如Phone_Number包含“123-444-4444,123-333-3334”等。

无论如何,后来的设计不是最理想的 - 你继续把它带到最后的水平并拥有“一个真实的查找表”http://www.dbazine.com/ofinterest/oi-articles/celko22或实体属性值http://tonyandrews.blogspot.com/2004/10/otlt-and-eav-two-big-design-mistakes.html

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10678084117056

无论哪种方式,这几乎总是一件坏事。虽然它们可能共享一个共同的数据类型/域,但意味着不同 - 因此它们应该保持单独的属性(maxtemp,mintemp等)。

答案 1 :(得分:1)

我认为(并且被教导)1NF为“所有行应该是相同的长度”而不是“没有重复的组”。根据该视图,您可以从以下内容中轻松做出决定:

在设计1中,两种测试是否始终存在?如果是这样,那么它不是真正的重复组。设计2中是否所有平均值都存在?在给定的行中可能会有更多(或更少)吗?

在设计4中,这两个值总是存在吗?如果是这样,那很好。如果没有,则应使用设计5。

答案 2 :(得分:1)

这是关于重复群体的规则 - 什么是功能依赖的?

如果统计值在功能上依赖于SN,测试和统计名称,那么您有三个关键元素和一个值元素。 ( SN, Test, Statistic -> Value )

在这种特定情况下 - 聚合数据(均值,总和,最小值,最大值) - 你有歧义,因为你没有处理原子对象,你正在处理聚合。严格来说,你不应该存储聚合,你应该计算它们。 (是的,我知道这是不切实际的,但这是关系理论。)

对于其他情况,通常很明显什么是关键,什么是重复组的价值。然而,在这种情况下,由于存储可导出的数据,你处于阴暗的边缘。

对于您的示例,请按照数据仓库设计找到更实用的测试:

你会用另一把钥匙切片和骰子吗?

将您的统计事实视为由三个维度包围的点:(SN,测试,统计)。这有效吗? (使用摘要数据,它通常是模糊的。)

相反,让我们看一下我们应该保留的详细数据:SN,测试,分数。在这两个维度的交叉处显然有两个维度(SN,测试)和一个度量(得分)。我们可以使用维度(SN或测试)从这些详细数据中获得任意数量的统计数据

对于电池示例,您可能执行想要将其创建为EAV数据库而不是更典型的关系数据库。您的测量(AvergaeCurrent和BatteryCapacity)为您提供了使用Entity-Attribute-Value数据库设计的充分理由。

请注意,所有关系设计都是较长关系和EAV三元组之间的张力。您必须始终平衡“这是关键”与“这是一个列”,因为您始终可以将所有内容标记为属性键并使用EAV设计。

答案 3 :(得分:1)

设计应根据您的用例场景和您预期的查询类型来确定。你打算做很多读,写或大量更新吗?您是希望获得候选人的整个测试数据,还是希望获得最佳测试或其他内容。您最常运行的是什么查询?

设计1

SN     Test1_Max   Test1_Min    Test1_Mean  Test2_Max   Test2_Min    Test2_Mean
2093      23          2            15         54          -24           45  

这在性能方面是最好的。它不需要JOIN。如果字段的数量是确定性的而不是任意的(例如每个人最多有两个测试分数),那么如果您决定将两个以上的测试分数与一个人相关联,这样会更好。由于每行的SN为unique,因此数据库引擎一旦找到匹配就可以返回,这是性能更好的另一个原因。

设计2

SN     Test      Max    Min    Mean     
2093    1        23     2      15       
2093    2        54     -24     45      

如果SN 2093可以在其个人资料中进行N次测试,这非常有用。同样地,如果测试的数量是10米,那么这个设计也比拥有30列更好。每个查询和比较都会很重。如果您的应用程序需要查询,以便为学生2093获得性能最佳的测试,或者如果愿意对测试分数进行一些分析和报告,这也很有用。这比前一个稍微慢一些,但更灵活。我更喜欢这个,因为我有预感,你可能会对测试统计感兴趣,学生可以分别进行两次以上的测试。

设计3

SN     Test    Statistic    Value
2093    1        Max          23
2093    1        Min          2
2093    1        Mean         15       
2093    2        Max          54
2093    2        Min         -24
2093    2        Mean         45  

如果您的查询对值的兴趣超过任何值,那么这非常有用。例如,如果您对多少值大于80感兴趣,这将是快速的。在您的方案中,这没有意义。你最终会做太多的自我加入。读取会很慢!但是,写入可能会更快,因为您可以快速更新SN 2093Test 2的最大分数(假设统计列是枚举而不是字符串,因为字符串比较可能很昂贵)。

设计4

SN      AverageCurrent (mA)    BatteryCapacity (mA)  
2093          200                    540  

设计5

SN      mA_Measuremnt       Value
2093    AverageCurrent      200 
2093    BatteryCapacity     540 

同样的论点适用。这真的取决于您是否打算优化读取或写入?例如,对于Web应用程序,如果您可以使用它,我更喜欢Design 1.例如,我通常会知道用户最多只能拥有3个电话号码,因此我将使它们成为用户列中的一个字段,避免加入。尽管写入需要将某些字段设置为null,但读取速度很快。

答案 4 :(得分:0)

当你确定'测试'将(永远)只有最大值,最小值和平均值时 - >使用设计2。 但是,如果未来有可能出现新的“统计数据”,最好使用设计3。

回答:

  

你是否应该只删除并标准化重复组,如果它们是相同的域并具有完全相同的含义?

虽然在许多书中,看起来这些正常形式是严格定义的,但事实并非如此。您应该在自己的应用程序中看到最佳解决方案是什么...规范化太多并不总是最佳解决方案,尤其是当您发现总是将所有数据重新连接在一起时。

答案 5 :(得分:0)

我建议将重复组仅移动到单独的表中,如果它们具有可变长度。如果您只有Phone1,Phone2和Phone3,则无需将它们分开。在另一种情况下,如果重复次数变化,则更好的设计是单独的表。

你的完全相同的领域和意义的概念不是很直观,因为它取决于抽象的程度。 Phone1与Phone2不完全相同,但它们都是电话号码。您还可以创建一个表AddressDetails并在那里移动电话号码。但也有名称,街道和城市 - 它们都是地址细节。您必须在通用键值对和专用列之间找到一种方法。

答案 6 :(得分:0)

如果您在CustID上有PK,则设计1实际上是1NF。如果除了PK之外没有数据依赖于任何数据,它可能在3NF中。其他CustID不会重复Phone1。

如果没有您尝试解决的业务案例,则无法决定模型。因此,设计1可能是完全有效的逻辑模型。

答案 7 :(得分:0)

这些示例违反第一范式。第一范式不禁止多个相同类型的列。

第一范式是一个不同的问题 - 它是关于如何将数据模型从非关系数据库形式(我们今天可能称之为 NoSQL 数据库)转换为关系形式

“重复组”是分层数据库系统中的一个旧概念,其中记录可以嵌套:一个记录可以有一个字段,而该字段又包含多个另一种类型的记录。嵌套在另一个记录中的记录集合称为重复组。为了将这种结构转化为关系形式,需要将重复的组提取到单独的表中,然后通过外键关联起来。

关系数据库系统通常不支持嵌套记录,因此默认情况下数据将在 1NF 中。 1NF 还要求所有列都具有唯一名称,这也是由数据库引擎强制执行的。

将三个电话号码作为三个单独的列的设计因此不违反 1NF。当然,期望每个客户只有三个电话号码有点奇怪,但这只是业务规则的问题,与规范化无关。如果所有客户确实只有三个电话号码,那么与使用单独表格的设计相比,具有三列的设计更安全且更易于管理。例如,如果数字位于单独的表中,则很难对每个客户强制执行精确的树号约束。

但是除了 1NF 之外,可能还有其他使用单独表格的方法。

如果电话号码的数量是无限的,那么显然有必要把它变成一个单独的表格。电话号码的列数不能无限制。

如果电话号码的数量因客户而异,那么将其作为单独的表格进行管理可能也更容易。您可以为电话号码设置三个可为 null 的字段,但这会很混乱,因为它们可以以任意组合包含 null 和非 null。

在第二个例子中,设计 1 没有违反 1NF,但设计 2 似乎仍然更便于管理。尤其是因为您(我假设)将要进行 2 次以上的测试。

不过,设计 3 的问题更大。它是 EVA(实体-属性-值)反模式的一个示例。在关系表中,字段的类型和语义应由其列给出。但是在这个设计中,“值”的类型和语义是由另一个字段“统计”给出的。不同的统计数据不会具有相同的值类型(例如,'count' 将是一个整数,'average' 将是一个浮点值)。此外,想象一下必须编写一个查询,该查询返回所有 SN 的最小值和最大值之间的差异。如果统计数据是列,这将是微不足道的,但这种设计非常复杂。

与设计 4 相比,设计 5 具有相同的问题。列名称“值”始终是一个危险信号,因为它表明该列没有单一含义。

公平地说,EVA 设计在某些特定场景中有其用途。但可以肯定地说,如果您有疑问,请避免它。