具有可疑重复组的数据库表设计示例

时间:2009-12-01 13:35:21

标签: database-design normalization

我正与某人讨论下表,该表用于链接特定于客户的项目:

Table LINK:

Client (int) 
Item1 (int) 
Item2 (int)

这是有争议的设计。所有三个字段都指向其他表。两个Item字段引用相同的其他表。这些不是真正的字段名称,所以不要讨论命名约定(“1”和“2”实际上是字段名称的一部分)。我认为这种设计在违反1NF的情况下是不好的,而另一个人则认为即使这看起来令人反感,所有其他选择对我们的特定用例来说也更糟。

注意:

  • 绝大多数情况只需要将两个项目相互链接;
  • N:允许1组;在这种情况下,在具有不同Item2值的多行上重复相同的Item1;
  • 还有极少数情况下,某些Item2值(在现有的Item1-Item2链接中)本身链接到其他Items,在这些情况下,这些值出现在Item1列中,其他链接值在Item2列;所有链接的项目都对应一个组,必须按原样检索。

我的主张:

  • 这违反了1NF:Item1和Item2是同一个表的外键,因此构成一个重复组(另一方对重复组的定义不同意);
  • 对于Item上的搜索,这意味着需要两个索引而不是一个索引,例如在使用GroupID字段的表中;
  • 这使查询此表中特定项的查询更复杂,因为限制子句必须同时检查Item1和Item2字段。
  • 检索Item链接链的情况会更复杂。

另一方声称:

  • 最可行的替代方案是具有单个Item字段的表,以及另外的GroupID字段;
  • 现在,更简单,更常见的双项链接案例变得更加复杂;
  • 获取GroupID插槽时可能存在并发问题,需要进行管理
  • 管理GroupID并发问题可能需要在具有唯一性约束的字段中使用GroupID的第二个表
  • 您现在必须至少在某些时候执行连接,尤其是在使用ORM的情况下。与当前设计中的单个表一样,连接效率较低。

我想听听一些有关此事的意见。我已经阅读了有关数据库设计的其他帖子,尤其是1NF,但是他们没有像我希望的那样专门处理上面的案例。基于网上的大量研究,我也开始明白,不同的人可以通过许多不同的方式来定义像1NF这样的所谓标准。我试图尽可能清楚地了解这两个论点,而不是偏向于其中一个。

编辑1:

  • 第1项和第2项是(财务)交易
  • “1”和“2”实际上是字段名称
  • 的一部分

2 个答案:

答案 0 :(得分:2)

什么是Item1和Item2?它们是不同的实体吗?那么这个设计对我来说很好。

例如,您可能希望使用解决旅行商问题的解决方案来填充数据库。你有一个表City(cityId,纬度,经度)和一个表Path(pathId,salesmanId)。现在,销售人员访问n + 1个城市的路径将由PathSegment(pathId,segmentId,fromCityId,toCityId)中的n个条目表示。这里,尽管fromCityId和toCityId是引用相同表City的外键,但它们描述了PathSegment实体的不同属性,因此这不违反NF1。

编辑:

所以你想要存储树,实际上,只有你的树大多只是链表,而且大多数只是两个节点的链表,对吧?显然你的同事想把它作为一个邻接列表,所以像

这样的树
1-2-3
\-4

变为

(1,2)
(2,3)
(1,4)

这没有任何问题,但这不是将树存储在数据库中的唯一方法。有关替代方案的总结,see here

在您的情况下,使用邻接列表的优点是您的大多数树只有两个节点,因此大多数树最终成为表中的一行,保持这一点。此外,关于邻居的问题很容易。 “这笔付款的发票是什么?”变

select item1 from link where item2 = :paymentID

也很整洁。但是有一些缺点。子节点的顺序通常很重要,列表对此没有帮助,因此您必须将其存储为单独的列或外键所指的表中的时间戳。此外,重建整个分支成为递归任务,并非所有数据库系统都可以这样做。因此,如果您的应用程序经常需要检索类似于消息板的发票历史记录概述,则可能需要一些应用程序端逻辑将相邻节点的列表转换为客户端上的树并对其进行处理。如果这变得太麻烦,您可能需要考虑嵌套集表示see here

什么是最适合您的问题?取决于几个方面:树的大小和形状(如果它们实际上是短链接列表,邻接列表是好的),插入和更新的频率(如果频繁,邻接列表是好的,因为它的插入是便宜的),频率和查询的复杂性(如果频繁和复杂,嵌套集很好,因为它的选择简单而快速)。所以对于留言板,我会使用嵌套集(甚至Tropashkos nested intervals来获得速度和额外的酷感),但是对于一个简单的请求 - 响应(有时是更多响应)表,我可能会使用邻接清单。

答案 1 :(得分:1)

只有两个指向同一个表的外键默认情况下不是“违规”。您可能有一个Person表,其中的FatherID和MotherID字段都指向Person表。这不是重复组,因为它们在语义上是不同的属性。您的第一个索赔 - 无论是书面的还是没有任何其他背景 - 都是错误的。