13

要遵守第一范式,您必须避免的一件事是重复组。如代替:

    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

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

设计一

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

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

设计二

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),但它们代表了关于组件的非常不同的事物。在这种情况下,设计 4 是否更好,因为它不是严格意义上的重复组?我想我正在寻找一些标准来知道何时将其分解为更多表格,从而使其更加垂直。

总结一下这个长得可笑的问题,如果重复组是完全相同的域并且具有完全相同的含义,您是否应该只删除和规范化它们?. 如果是这样,那么实际上只有电话示例和设计 1 中的两个测试符合此标准。虽然看起来设计 3 和 5 可能有设计上的好处,尽管设计 3 的统计数据严格来说有不同的含义,AverageCurrent 和 BatteryCapacity 在设计 5 中肯定有不同的含义。

4

8 回答 8

7

设计 2 和设计 4 是最好的方法,前提是结果并不总是存在(也就是设计 1 中的 NULL)。如果它们总是被采用,那么第一个设计就可以了。

我相信如果你有一列塞满了附加值,我相信 SQL 中的重复组实际上是这样的,例如 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 等)

于 2009-06-04T15:42:54.003 回答
1

我认为(并且被教导)1NF 为“所有行都应该是相同的长度”,而不是“没有重复的组”。有了这种观点,您可以从以下方面更轻松地做出决定:

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

在设计 4 中,这两个值是否始终存在?如果是这样,那很好。如果不是,则应使用设计 5。

于 2009-06-04T15:43:39.477 回答
1

这是重复组的规则——什么是功能依赖的?

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

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

对于其他情况,重复组的键和值通常是显而易见的。但是,在这种情况下,您处于模糊的边缘,因为您存储了可导出的数据。

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

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

把你的统计事实想象成一个被三个维度包围的点:(SN、Test、Statistic)。这是有效的吗?(对于汇总数据,它通常是模糊的。)

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

对于电池示例,您可能确实希望将其创建为 EAV 数据库,而不是更典型的关系数据库。您的测量值(AvergaeCurrent 和 BatteryCapacity)为您提供了使用实体-属性-值数据库设计的充分理由。

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

于 2009-06-04T15:55:34.267 回答
1

设计应由您的用例场景和您预期的查询类型决定。您要进行大量读取、写入或大量更新吗?您是希望获得候选人的全部测试数据,还是只希望获得最好的测试或其他东西。您将最频繁地运行什么查询?

设计一

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

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

设计二

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

SN 2093如果可以在他们的配置文件中有 N 个测试,这很有用。同样,如果测试的数量是 10m,那么这种设计也比 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 2093and的最大分数Test 2(假设 Statistic 列是枚举而不是字符串,因为字符串比较可能很昂贵)。

设计 4

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

设计5

SN      mA_Measuremnt       Value
2093    AverageCurrent      200 
2093    BatteryCapacity     540 

同样的论点也适用。这真的取决于您是否打算针对读取或写入进行优化?例如,对于 Web 应用程序,如果你能侥幸成功,我更喜欢设计 1。例如,我通常会知道一个用户最多只有 3 个电话号码,所以我会将它们分别设为用户列中的一个字段,并且避免加入。即使写入需要将某些字段设置为空,读取速度也很快。

于 2009-06-04T16:02:51.830 回答
0

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

一个答案:

如果它们是完全相同的域并且具有完全相同的含义,您是否应该只删除和规范化重复组?

尽管在许多书中,这些范式似乎是严格定义的,但实际上并非如此。您应该为自己的应用程序查看最佳解决方案是什么……规范化过多并不总是最佳解决方案,尤其是当您看到您总是将所有数据重新组合在一起时。

于 2009-06-04T15:36:11.163 回答
0

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

而且您对完全相同的域和含义的概念不是很直观,因为它取决于抽象级别。Phone1 与Phone2 并不完全相同,但它们都是电话号码。您还可以创建一个表 AddressDetails 并将电话号码移到那里。还有名称、街道和城市——它们都是地址详细信息。您必须在通用键值对和专用列之间找到一种方法。

于 2009-06-04T15:44:45.267 回答
0

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

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

于 2009-06-04T15:51:37.713 回答
0

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

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

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

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

因此,将三个电话号码作为三个单独的列的设计不违反 1NF。当然,期望每个客户恰好拥有三个电话号码有点奇怪,但这只是业务规则的问题,而不是规范化问题。如果所有客户确实都拥有三个电话号码,那么与单独的表格相比,带有这些列的设计既更安全,也更易于管理。例如,如果数字在单独的表中,则很难强制执行每个客户的确切树数的约束。

但是除了 1NF 之外,可能还有其他侦察可以使用单独的表。

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

如果电话号码的数量因客户而异,则作为单独的表格也可能更容易管理。您可以为电话号码设置三个可为空的字段,但这将是一团糟,因为它们可以有任何组合的空值和非空值。

在第二个示例中,设计 1 没有违反 1NF,但设计 2 似乎仍然更便于管理。特别是因为您(我假设)将在路上进行超过 2 次测试。

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

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

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

于 2021-06-07T09:44:18.620 回答