多对多表中的一两个主键?
我的数据库中有以下表,它们具有多对多关系,这由一个连接表表示,该连接表具有指向每个主表的主键的外键:
- 窗口小部件:WidgetID(PK),标题,价格
- 用户:UserID(PK),名字,姓氏
假设每个User-Widget组合都是唯一的。我可以看到两个有关如何构造定义数据关系的连接表的选项:
- UserWidgets1:UserWidgetID(PK),WidgetID(FK),UserID(FK)
- UserWidgets2:WidgetID(PK,FK),UserID(PK,FK)
选项1的主键只有一列。但是,这似乎是不必要的,因为存储在表中的唯一数据是两个主表之间的关系,并且该关系本身可以形成唯一键。因此,导致选项2具有两个列的主键,但丢失了选项1具有的一列唯一标识符。我还可以选择向第一个表添加一个两列的唯一索引(WidgetID,UserID)。
两者在性能方面是否有真正的区别,还是有任何理由在构建UserWidgets多对多表时偏爱一种方法而不是另一种方法?
解决方案
回答
由于每个User-Widget组合都是唯一的,因此应通过使组合唯一来在表中表示该组合。换句话说,使用选项2. 否则,我们可能会有两个条目,它们具有相同的窗口小部件和用户ID,但具有不同的用户小部件ID。
回答
在任何一种情况下,我们只有一个主键。第二个是所谓的复合键。没有充分的理由介绍新的专栏。实际上,我们将必须在所有候选键上保留唯一索引。添加新列只会给我们带来维护开销。
选择选项2.
回答
不需要第一个表中的userwidgetid,就像我们说的那样,唯一性来自widgetid和userid的组合。
我将使用第二个表,保留foriegn键,并在widgetid和userid上添加唯一索引。
所以:
userwidgets( widgetid(fk), userid(fk), unique_index(widgetid, userid) )
由于不需要数据库额外的主键,因此可以获得一些性能上的优势,因为数据库无需计算该键的索引。在上面的模型中,尽管仍然计算了该索引(通过unique_index),但是我认为这更容易理解。
回答
在这种情况下,主键有什么好处?考虑没有主键的选项:
UserWidgets3:WidgetID(FK),UserID(FK)
如果要唯一性,请使用复合键(UserWidgets2)或者唯一性约束。
具有主键的通常性能优势是我们经常通过主键查询表,这是快速的。对于多对多表,通常不会通过主键查询,因此没有性能优势。多对多表是通过其外键查询的,因此我们应该考虑在WidgetID和UserID上添加索引。
回答
选项2是正确的答案,除非我们确实有充分的理由添加代理数字键(在选项1中已完成)。
替代数字键列不是"主键"。从技术上讲,主键是唯一标识表中记录的列的组合之一。
建立数据库的任何人都应阅读Josh Berkus的这篇文章http://it.toolbox.com/blogs/database-soup/primary-keyvil-part-i-7327,以了解替代数字键列和主键之间的区别。
以我的经验,向表中添加替代数字键的唯一真实原因是主键是否为复合键,并且需要用作其他表中的外键引用。只有这样,我们才应该考虑在表中添加额外的列。
每当我看到一个数据库结构,其中每个表都有一个" id"列时,它很可能是由不了解关系模型的人设计的,它总是会显示Josh文章中指出的一个或者多个问题。
回答
我同意先前的回答,但我要补充一点。
如果要向该关系添加更多信息并允许在相同两个实体之间建立更多关系,则需要选项一。
例如,如果要跟踪用户1在userwidget表中使用小部件664的所有时间,则userid和widgetid不再是唯一的。
回答
就我个人而言,出于以下原因,我将在多对多表中使用合成/代理键列:
- 如果我们在实体表中使用了数字合成键,则在关系表上使用数字合成键可以保持设计和命名约定的一致性。
- 将来可能会发生这样的情况:多对多表本身成为下级实体的父实体,该下级实体需要对单个行的唯一引用。
- 并不会真的使用那么多的额外磁盘空间。
合成键不能替代自然/复合键,也不能成为该表的" PRIMARY KEY",仅因为它是表中的第一列,所以我部分同意Josh Berkus的文章。但是,我不同意自然键始终是'PRIMARY KEY's的良好候选者,如果将其用作其他表中的外键,则肯定不应该使用自然键。
回答
选项2使用简单的compund键,选项1使用代理键。在大多数情况下,选项2是首选,并且由于它是一个很好的候选密钥,因此它与基本模型很接近。
在某些情况下,我们可能需要使用代理键(选项1)
- 我们并不是随着时间的推移,复合键是一个不错的候选键。特别是对于时间数据(随时间变化的数据)。如果要向具有相同UserId和WidgetId的UserWidget表中添加另一行怎么办?考虑就业(EmployeeId,EmployeeId)-在大多数情况下都可以使用,除非以后有人再为同一雇主工作
- 如果我们要创建消息/业务交易或者类似的东西,需要使用更简单的密钥来进行集成。复制也许?
- 如果我们想创建自己的审核机制(或者类似的审核机制)并且不想密钥太长。
根据经验,在对数据建模时,我们会发现大多数关联实体(很多)是事件的结果。人员开始工作,将物品添加到购物篮等。大多数事件在时间上与事件有关,在某些情况下,日期或者时间是相关的,在这种情况下,替代键可能是最佳选择。
因此,采用选项2,但请确保我们具有完整的模型。
回答
我会两者兼而有之。
听我说:
复合键显然是一种不错的,正确的方法,可以反映数据的含义。没有问题。
但是:除非我们使用单个生成的主键作为替代键,否则使休眠正常工作会遇到各种麻烦。
因此,我将使用逻辑和物理数据模型。逻辑键具有复合键。实现逻辑模型的物理模型具有代理键和外键。