代码之家  ›  专栏  ›  技术社区  ›  JoeCool

DB设计:第一范式和重复组

  •  14
  • JoeCool  · 技术社区  · 7 年前

    要坚持第一范式,必须避免重复分组。例如,而不是:

        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
    

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

    设计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轻松地进行选择或连接,这与Design 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中肯定有不同的含义。

    7 回复  |  直到 6 年前
        1
  •  7
  •   Matt Rogish    17 年前

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

    我相信SQL中的重复组实际上是一个填充了add'l值的列,例如Phone_Number包含“123-444-4444123-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等)

        2
  •  1
  •   workmad3    17 年前

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

    在设计4中,这两个值是否始终存在?如果是的话,没关系。如果不是,则应使用设计5。

        3
  •  1
  •   S.Lott    17 年前

    这是关于重复组的规则——什么是功能相关的?

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

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

    在其他情况下,重复组的键和值通常是显而易见的。然而,在这种情况下,由于您存储的是可派生数据,因此您处于模糊边缘。

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

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

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

    以电池为例,您可能 做 Entity-Attribute-Value 数据库设计。

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

        4
  •  1
  •   aleemb    17 年前

    设计应该由您的用例场景和您预期的查询类型决定。你会做大量的读、写或更新吗?你是想得到一个候选人的全部测试数据,还是只想得到最好的测试结果。您最常运行的查询是什么?

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

    就性能而言,这是最好的。它不需要连接。如果字段的数量是确定的,而不是任意的(例如每个人最多有两个测试分数),那么如果您决定将两个以上的测试分数与一个人关联,那么这会更好,尽管会更严格。因为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 2093 和 Test 2 (假设统计列是枚举而不是字符串,因为字符串比较可能代价高昂)。

    设计4

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

    SN      mA_Measuremnt       Value
    2093    AverageCurrent      200 
    2093    BatteryCapacity     540 
    

    同样的论点也适用。这真的取决于您是否打算优化读写?例如,对于web应用程序,如果您可以不受影响,我更喜欢Design 1。例如,我通常知道一个用户最多只有3个电话号码,因此我会将每个电话号码都设置为用户列中的一个字段,并避免加入。即使写操作需要将某些字段设置为null,读取速度也很快。

        5
  •  0
  •   Fortega    17 年前

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

    对以下问题的答复:

    如果重复组确实是同一个域并且具有完全相同的含义,那么您是否应该删除并规范化它们?

    虽然在许多书中,这些规范形式似乎都有严格的定义,但它们并没有。您应该了解您自己的应用程序的最佳解决方案是什么。。。规范化太多并不总是最好的解决方案,尤其是当您看到总是将所有数据重新连接在一起时。

        6
  •  0
  •   Daniel Brückner    17 年前

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

        7
  •  0
  •   wqw    17 年前

    如果您在CustID上有PK,则设计1实际上是1NF。如果没有数据依赖于PK之外的任何东西,则可能是3NF,例如,Phone1没有为其他CustID重复。

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

        8
  •  0
  •   JacquesB    5 年前

    例如 不

    第一个范式是一个不同的问题——它是关于如何将数据模型从 非关系

    “重复组”是分层数据库系统中的一个老概念,其中记录可以

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

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

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

    如果电话号码的数量是无限的,那么显然有必要将其转换为一个单独的表。电话号码的列数不能无限。

    如果电话号码的数量因客户而异,那么作为一个单独的表来管理可能也会更容易。电话号码可以有三个可为null的字段,但这将是一个混乱,因为它们可以有null和non-null的任意组合。

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

    设计5与设计4存在相同的问题。列名“value”始终是红色标志,因为它表示该列没有单一含义。

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