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

Database design 具有可疑重复组的数据库表设计示例,database-design,normalization,Database Design,Normalization,我正在与某人讨论用于链接客户特定项目的下表: Table LINK: Client (int) Item1 (int) Item2 (int) 这是有争议的设计。所有三个字段都引用其他表。这两个项字段引用同一个表。这些不是真正的字段名,所以不要讨论命名约定(然而,“1”和“2”实际上是字段名的一部分)。我认为这种设计在违反1NF的情况下是不好的,而另一个人则认为,尽管这看起来令人讨厌,但对于我们的特定用例,所有其他选项都更糟糕 注: 绝大多数情况下,只需要将两个项目相互链接 N:1组,

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

Table LINK:

Client (int) 
Item1 (int) 
Item2 (int)
这是有争议的设计。所有三个字段都引用其他表。这两个项字段引用同一个表。这些不是真正的字段名,所以不要讨论命名约定(然而,“1”和“2”实际上是字段名的一部分)。我认为这种设计在违反1NF的情况下是不好的,而另一个人则认为,尽管这看起来令人讨厌,但对于我们的特定用例,所有其他选项都更糟糕

注:

  • 绝大多数情况下,只需要将两个项目相互链接
  • N:1组,但允许;在这种情况下,使用不同的Item2值在多行上重复相同的Item1
  • 也有极少数情况下,某些Item2值(在现有Item1-Item2链接中)本身链接到其他项目,在这些情况下,这些值出现在Item1列中,而其他链接值出现在Item2列中;所有链接项都对应于一个组,并且必须按此方式检索
我的主张:

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

  • 最可行的替代方案是一个具有单个项字段和附加GroupID字段的表
  • 更简单、更常见的两项链接案例现在变得更复杂
  • 获取GroupID插槽时可能存在并发问题,需要对其进行管理
  • 管理GroupID并发性问题可能需要在具有唯一性约束的字段中使用GroupID的第二个表
  • 现在必须执行连接,至少在某些时候,尤其是在使用ORM的情况下。联接的效率低于当前设计中使用单个表的效率
我想听听关于这方面的一些意见。我已经阅读了其他关于数据库设计的文章,尤其是1NF,但它们并没有像我希望的那样专门处理我的上述案例。根据网上的大量研究,我也逐渐了解到,像1NF这样的所谓标准可以由不同的人以多种不同的方式定义。我已经尽可能清楚地表达了这两种观点,而不是偏袒其中一种

编辑1:

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

项目1和项目2是什么?它们是不同的实体吗?那么我觉得这个设计很好

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

编辑:

所以你想存储树,实际上,只有你的树大部分都是链表,大多数都是只有两个节点的链表,对吗?很明显,你的同事想把它作为一个邻接列表来做

1-2-3
\-4
变成

(1,2)
(2,3)
(1,4)
这没什么错,但这不是在数据库中存储树的唯一方法。对于备选方案的良好总结

在您的例子中,使用邻接列表的优点是,大多数树只有两个节点,因此它们中的大多数最后都是表中的一行,保持简单。此外,关于近邻的问题也很容易回答。“这笔付款的发票是什么?”变成了

这也很好。但也有一些缺点。子节点的顺序通常很重要,而列表在这里对您没有帮助,因此您必须将其存储为单独的列或外键引用的表中的时间戳)。而且,重建整个分支成为一项递归任务,并非所有数据库系统都能做到这一点。因此,如果您的应用程序经常需要检索类似于消息板的发票历史概述,那么可能需要一些应用程序端逻辑,将相邻节点列表转换为客户端上的树并在该树上工作。如果这变得过于繁琐,您可能需要考虑嵌套集合表示,


什么对你的问题最好?取决于以下几点:树的大小和形状(如果它们实际上大多是短链表,则邻接列表很好)、插入和更新的频率(如果频繁,则邻接列表很好,因为它的插入很便宜)、查询的频率和复杂性(如果频繁且复杂,嵌套集是好的,因为它的选择既简单又快速。)因此对于留言板,我会选择嵌套集(甚至是Tropashkos,以提高速度和额外的冷静度),但对于简单的请求-响应(有时还有更多的响应)表,我可能会使用邻接列表。

只有两个外键指向同一个表在默认情况下不是“冲突”您可能有一个Person表,其中FatherID和MotherID字段都指向Person表。这不是重复组,因为它们是语义不同的属性。您的第一个声明是书面声明,没有任何其他内容
select item1 from link where item2 = :paymentID