我正与某人讨论下表,该表用于链接特定于客户的项目:
Table LINK:
Client (int)
Item1 (int)
Item2 (int)
这是有争议的设计。所有三个字段都指向其他表。两个Item字段引用相同的其他表。这些不是真正的字段名称,所以不要讨论命名约定(“1”和“2”实际上是字段名称的一部分)。我认为这种设计在违反1NF的情况下是不好的,而另一个人则认为即使这看起来令人反感,所有其他选择对我们的特定用例来说也更糟。
注意:
我的主张:
另一方声称:
我想听听一些有关此事的意见。我已经阅读了有关数据库设计的其他帖子,尤其是1NF,但是他们没有像我希望的那样专门处理上面的案例。基于网上的大量研究,我也开始明白,不同的人可以通过许多不同的方式来定义像1NF这样的所谓标准。我试图尽可能清楚地了解这两个论点,而不是偏向于其中一个。
编辑1:
答案 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表。这不是重复组,因为它们在语义上是不同的属性。您的第一个索赔 - 无论是书面的还是没有任何其他背景 - 都是错误的。