【问题标题】:Are multiple Many-Many relationships evidence of bad design?多个多对多关系是否是糟糕设计的证据?
【发布时间】:2012-07-24 14:49:18
【问题描述】:

大家好,

我一直在学习数据库和数据库设计,但我发现我仍然遇到一个我自己无法回答的问题。所以我向社区提出这个问题,希望有比我更多知识/经验的人来回答。

我的任务是开发一个数据库,该数据库跟踪整个船队的库存水平。

当前的设计为每艘船都有一个表格,其中列出了所有可能的部件(机械类型、部件号、制造商、序列号等)

这意味着一件机器或零件的细节可以被复制很多次(实际上有多少艘船就可以复制多少次)。

我一直在尝试根据自己学到的知识进行重新设计,我会提出如下设计方案:

[SHIP]
ID, Name, Class, Tonnage, Fleet, Superintendent etc.

[Machinery]
ID, Type, Make, Model etc. (Can have separate table for manufacturers and types if required)

[Part]
ID, Part number, Description, etc.

以上将是三个主要的表,现在开始变得困难。

每艘船可以有多个机械项目,每个机械项目可以出现在多艘船上(需要一个连接表)

每个机械项目可以有多个零件,每个零件可以属于多个机械项目(另一个连接表)

可能有成百上千个部分使连接表变得巨大。

此外,一旦您想跟踪库存,您就会查看另一个连接表

[Stock Level]
ShipID, PartID, Stock Level

另外,如果您想要最低库存(可以与 Stock Level 结合使用吗?)

[Min Stock]
ShipID, PartID, Min Stock

最后,如果您想要标准化数据库(即没有 Part No.1、Part No.2 或 Serial No.1、Serial No.2)

你需要多几张桌子

[Serial Numbers]
ShipID, MachineryID, Serial No

[Part Numbers]
PartID, Part Number

序列号可能是相当标准的,没有问题,但是 [零件号] 将需要至少与 [零件] 表中的记录一样多的记录。

地图(尽我所能在没有图片的情况下表示,为简单起见省略了路口)

  <>V represent many 
  -| represent one

   -----< Serial Numbers
  |             V
  |             |
Ship >---< Machinery >---< Parts ---< Part Numbers
  V                          V
  |                          |
   ------ Stock Level -------

现在真正的问题是,我是否在基本设计原则中遗漏了一些可以消除如此巨大的联结表的内容,或者这种数据库是否可以预期。

此外,在零件编号等情况下,规范化需要一个额外的表,其中至少有相同数量的记录,而不是原始表中的额外列,您以后会去规范化以提高查询速度吗?

任何关于外部资源(包括其他论坛、教程、书籍)的提示、技巧或指针将不胜感激。

欢迎所有答案,提前感谢您提供的任何帮助。

戴夫

【问题讨论】:

  • 巨大的联结表是正常的。如果说巨大是指几百万行,那并不是很大,而且它们很容易被索引。
  • 快速回答你们,这回答了我的一个问题。不过,我需要对索引进行一些研究。我认为索引是由 DBMS 自动完成的,但我会做一些调查,因为这可能对速度有很大帮助。

标签: database-design


【解决方案1】:

首先,:多对多关系并不是糟糕设计的指标。

其次,连接表总是大于它们正在连接的表。如果您有两个表,每个表有 100 条记录,则连接表最多可以有 10000 条记录。

但连接表只不过是整数对,当您为它们编制索引时,它们会非常快。

编辑添加:

你评论了:

我认为索引是由 DBMS 完成的

您可以告诉 DBMS 索引什么。之后,自动更新索引。

一些 DBMS 拥有一些工具,可以监控您的系统的运行情况,并就哪些索引将是有益的提供建议。

你的设计过程很好,所以你可能走在正确的轨道上。

【讨论】:

  • 我认为我的原始设计(加上更多的规范化)将或多或少是正确的方法。对于我的“相对较小”的数据库,索引联结表的执行速度足够快。
【解决方案2】:

除了 egrunin said 之外,如果您的 DBMS 支持,连接表通常也是 clustering (aka index-organized tables) 的理想候选者。

例如,聚类...

[Stock Level]
ShipID, PartID, Stock Level

...会将具有相同ShipID 的行存储在一起,这使得获取与给定船相关联的所有行非常有效。

另一方面,聚类...

[Stock Level]
PartID, ShipID, Stock Level

...将具有相同 PartID 的行存储在一起,从而可以有效地获取与给定部分关联的行。

如果您需要同时查询这两个方向,则需要 2 个索引({ShipID, PartID}{PartID, ShipID})。通常,聚簇表中的二级索引必须包含copy of the whole PK, requiring additional storage and causing double-lookup,这使得对具有多个索引的表进行聚簇的成本很高。然而,在这种情况下,我们无论如何都会覆盖相同的字段(只是以不同的顺序),因此二级索引中没有开销。您甚至可以考虑将covering Stock Level 与二级索引一起使用以避免双重查找(聚集索引已经自然地覆盖了它)。

如果您的 DBMS 不支持集群,请考虑索引 {ShipID, PartID, Stock Level}(或 {PartID, ShipID, Stock Level} 或两者)。这样,您的查询被索引覆盖,无需访问表堆。从本质上讲,您实现了集群的效果,除了在(冗余)表堆上浪费了额外的空间。


除此之外,部分 DBMS 还支持领先的index compression,大大减少了连接表索引中重复值的存储空间。压缩一个聚集表(它本身就是一个索引)以获得最大效果。

【讨论】:

  • 我玩过索引,它看起来很适合了解更多信息。目前我只是在使用我认为不支持集群的访问结构,但我还没有机会检查。当我在 MSSQL 中构建实际的数据库时,我对此还是比较陌生(到目前为止只制作了一个有用的数据库)。我想在这个数据库中使用 EF,所以我还有很多学习要做。但是,我会记住您所说的一切,并感谢您提供的链接。
  • @DaveWilliams 关于 MS SQL Server 的一个警告:PRIMARY KEY 约束default to CLUSTERED。如果您需要基于堆的表,则需要显式指定PRIMARY KEY NONCLUSTERED。仅仅因为集群对某些表有好处并不意味着它对所有人都有好处,而且当人们不注意时,这种语法怪癖会导致数据库过度集群和性能问题......
  • 我刚刚在 SSMS 中找到了该选项。埋得很深。我也将不得不进一步调查。感谢您给我提示并指出我正确的方向。这是一个复杂的野兽,但我一直认为最好的学习方式是通过使用:) 再次感谢。
猜你喜欢
  • 2013-07-15
  • 2021-04-28
  • 2010-11-19
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
相关资源
最近更新 更多