MySQL - 我应该在每个子表上使用多列主键吗?

时间:2011-09-08 20:46:40

标签: mysql database database-design database-schema mysql-management

设定:

当我在stackexchange上发现这篇精彩的文章时,我试图理解识别和非识别关系之间的区别。 What's the difference between identifying and non-identifying relationships?

在阅读了一些评论之后,我又想到了一个问题,我想到了一个问题。


问题:

我应该在每个子表上使用多列主键吗?这样做有哪些优点/缺点?

为了更好地说明我的问题,我在下面创建了一个示例。我还提到了让我提出这个问题的评论。


实施例

在我的情况下,我知道building_id,我需要bed.data

#1 - 我目前的数据库结构:

TABLE { FIELDS }
-----------------------------------------------------------------------
building { id, data } 
floor { id, building_id, data }
room {id, floor_id, data }
bed {id, room_id, data }

这种类型的表结构需要我使用一些连接来获取我需要的数据。我遇到了很多这样的情况,但并不是什么大不了的事情。

#2 - 我对Bill Karwin建议的数据库结构的解释(参见下面的文章评论):

TABLE { FIELDS }
-----------------------------------------------------------------------
building { id, data } 
floor { id, building_id, data }
room {id, building_id, floor_id, data }
bed {id, building_id, floor_id, room_id, data }

这种表结构似乎在我的情况下消除了连接的需要。那么这个表结构的缺点是什么?我真的很喜欢不做这么多连接声明的想法。


来自文章的评论:

  

What's the difference between identifying and non-identifying relationships?

     

@hobodave:这是“约定优于配置”参数。有些思想认为,每个表都应该为一个名为id的单列伪代码定义其主键,以自动生成其值。像Rails这样的应用程序框架已将其推广为默认值。它们将自然键和多列键视为与使用“遗留”数据库时所需的约定不同。许多其他框架都遵循这一主导。 - Bill Karwin 2010年3月10日23:06

     

似乎“正确”构建识别关系会导致令人讨厌的巨大主键。例如大楼有地板有房间有床。床的PK将是(bed_id,floor_id,room_id,building_id)。看起来很奇怪,我从未在实践中看到这一点,也没有听说它是一种做任何事情的方式。这是PK中的大量冗余数据。 - hobodave 2010年3月10日23:34

     

@hobodave:我见过更多的多列主键。但我明白你的观点。考虑多列主键传达更多信息;您可以查询床位,了解特定建筑物中的所有床位,而无需进行任何连接。 - Bill Karwin 2010年3月11日凌晨1点

3 个答案:

答案 0 :(得分:3)

此数据已标准化

TABLE { FIELDS }
-----------------------------------------------------------------------
building { id, data } 
floor { id, building_id, data }
room {id, floor_id, data }
bed {id, room_id, data }

这张表不是(坏主意)

TABLE { FIELDS }
-----------------------------------------------------------------------
building { id, data } 
floor { id, building_id, data }
room {id, building_id, floor_id, data }
bed {id, building_id, floor_id, room_id, data }
  1. 在第一个(好)表中,您没有不需要的重复数据。
  2. 第一个表格中的插入内容会快得多。
  3. 第一个表格更适合内存,加快查询速度。
  4. InnoDB在考虑模型A的情况下进行了优化,而不是模型B.
  5. 后一个(坏)表有重复数据,如果不同步,你就会乱七八糟。 DB A不会更难同步,因为数据只列出一次。
  6. 如果我想要从建筑物,地板,房间和床上组合数据,我需要将模型A和模型B中的所有四个表组合在一起,如何在这里节省时间。
  7. InnoDB将索引数据存储在自己的文件中,如果select仅索引,表格本身将从不访问。那你为什么要复制索引呢?无论如何,MySQL永远不需要读取主表。
  8. InnoDB将PK 存储在每个二级索引中,使用复合而且长PK,您正在减慢每个使用索引并对文件大小进行压缩的选择;什么都没有获得。
  9. 你有严重的速度问题吗?如果没有,你是在正规化你的桌子吗?
  10. 甚至不考虑使用受这些问题影响较小的MyISAM,它没有针对多连接数据库进行优化,也不支持参考intregrity或事务,并且与此工作负载不匹配。
  11. 使用复合键时,您只能使用键的最右侧部分,即除了使用floor_id之外,您无法在表bed中使用id+building_id+floor_id,这意味着您可能必须使用比模型A中所需更多的键空间。或者你需要添加一个额外的索引(它将拖动PK的完整副本)。
  12. 简而言之
    我在模型B中看到绝对零利益和许多缺点,从不使用它!

答案 1 :(得分:3)

我认为你的#2不太可能是Bill Karwin的意思。通常,“id”表示自动数字序列。我认为他更有可能在这些方面表达意义。构成主键的列位于星号之间。

TABLE    { COLUMNS }
-----------------------------------------------------------------------
building { *building_id*, other columns } 
floor    { *building_id, floor_num*, other columns }
room     { *building_id, floor_num, room_num*, other columns }
bed      { *building_id, floor_num, room_num, bed_num* (?), other columns }

我不确定你可能会为“床”提供哪些其他专栏。双床房,全套房,大床房,特大床房这可能有意义。如果是这样的话,那么这个表

bed      { *building_id, floor_num, room_num, bed_num*, bed_size }

远非“非规范化”。事实上,它在5NF。

如果你测试这两个模式的性能,你可能会发现这个模式在大多数情况下绕着#1运行。在我运行的一批查询中,它的速度提高了大约30倍。

答案 2 :(得分:0)

第一个表格结构是规范化的,经典的结构。但不幸的是,这个不适用于大项目。因为如果您的表构建包含许多数据行,例如百万取决于您使用加入的城市或国家/地区将非常缓慢。 因此在实际项目中使用包含所有聚合信息的非规范化表。您可以直接使用此类表,也可以使用sphinx等独立服务器来搜索数据。关于三个领域的主键,我认为在这种情况下,这个是多余的。因为

  1. 如果您使用innodb,则此密钥将添加到此表中的所有辅助密钥。
  2. 如果使用界面来管理床位,使用一个字段ID来处理特定行比使用三个字段更方便。
  3. 如果您想保证行的唯一性,可以在这3个字段中使用UNIQUE KEY。