为什么我在使用复合键时阅读了这么多负面意见?

时间:2010-08-18 13:59:48

标签: sql ms-access primary-key composite-key composite-primary-key

我正在开发一个喜欢自动编号标识符的Access数据库。每个表都使用它们,除了一个,它使用由一个人的名字,姓氏和出生日期组成的密钥。无论如何,人们开始遇到很多重复问题,因为表示关系的表可以保持两次或更多次相同的关系。我决定通过为关系表实现复合键来解决这个问题,因此我没有遇到重复问题。

所以我想知道Access世界中复合键的错误代表是什么?我想编写查询会稍微困难一些,但至少每次输入数据或在前端编辑数据时都不需要进行大量的检查。他们是非常低效还是什么?

9 个答案:

答案 0 :(得分:12)

复合键适用于单个表,但是当您开始在表之间创建关系时,它可以获得更多。

考虑两个表PersonEvent,以及它们之间的多对多关系,称为Appointment

如果Person表中的复合键由名字,姓氏和出生日期组成,并且Event表中的合成地址键由地点和名称组成,则表示将在Appointment表中获得五个字段以识别关系。

绑定关系的条件将很长:

select Person,*, Event.*
from Person, Event, Appointment
where
  Person.FirstName = Appointment.PersonFirstName and
  Person.LastName = Appointment.PersonLastName and
  Person.BirthDate = Appointment.PersonBirthDate and
  Event.Place = Appointment.EventPlace and
  Event.Name = Appointment.EventName`.

另一方面,如果您有PersonEvent表的自动编号密钥,则只需要Appointment表中的两个字段来标识关系和条件要小得多:

select Person,*, Event.*
from Person, Event, Appointment
where
  Person.Id = Appointment.PersonId and Event.Id = Appointment.EventId

答案 1 :(得分:7)

如果您只使用纯自编SQL来访问您的数据,那么就可以了。

但是,某些ORM,适配器等需要一个PK字段来标识记录。

另请注意,复合主键几乎总是一个自然键(创建代理复合键几乎没有意义,您也可以使用单字段键。)

复合主键的最常见用法是多对多链接表。

使用自然键时,您应确保它们固有的唯一不可变,即实体始终由键的相同值标识,由模型反映,任何值都只能识别一个实体。

在你的情况下情况并非如此。

首先,一个人可以改变他们的名字甚至是生日

其次,我很容易想象在同一天出生的两个John Smiths

前者意味着如果某人更改了他们的名字,则必须在引用persons的每个表中更新它;后者意味着第二个John Smith将无法进入您的数据库。

对于像你这样的情况,我真的会考虑为你的模型添加一个代理标识符。

答案 2 :(得分:4)

不幸的是,这些负面意见的一个原因可能是无知。太多人不能正确理解候选密钥的概念。有些人似乎认为每个表只需要一个密钥,一个密钥足以保证数据的完整性,选择一个密钥就足够了。

我经常推测,完全弃用和逐步淘汰“主键”一词是一件好事。这样做会使数据库设计人员关注真正的问题:表必须具有确保数据正确性所需的密钥,并且其中一些密钥可能是复合的。废除主要关键概念将消除所有关于主键应该是什么的愚蠢辩论。

答案 3 :(得分:3)

如果您的RDBMS支持它们并且如果您正确使用它们(并且一致),则复合PK上的唯一键应足以避免重复。至少在SQL Server中,您还可以针对唯一键而不是PK创建FK,这可能很有用。

单个“id”列(或代理键)的优点在于可以通过制作更窄的密钥来提高性能。由于此密钥可以被传送到该表上的索引(作为指向索引行的物理行的指针)而其他表作为FK列可以减少空间并提高性能。其中很大程度上取决于RDBMS的特定体系结构。不幸的是,我对Access的评论不够熟悉。

正如Quassnoi所指出的,一些ORM(以及其他第三方应用程序,ETL解决方案等)无法处理复合键。除了一些ORM,最新的第三方应用程序值得支持复合键。尽管如此,ORM在采用它方面的速度要慢一些。

我个人对复合键的偏好是虽然一个独特的索引可以解决重复的问题,但我还没有看到一个实际上完全使用它们的开发商店。大多数开发人员都对此很懒散。他们抛出一个自动递增的ID并继续前进。然后,在接下来的六个月里,他们付了我很多钱来解决重复的数据问题。

另一个问题是,自动递增ID通常不是可移植的。当然,你可以在系统之间移动它们,但由于它们在现实世界中没有实际的基础,因此无法确定一个关于实体的其他内容。这在ETL中变得很重要。

PK在数据建模领域是一件非常重要的事情,如果你希望你的数据保持一致和干净,它们通常需要更多的思考,“添加一个自动递增的ID”。

代理键也很有用,但是当我遇到一个我正在尝试处理的已知性能问题时,我更喜欢使用它们。否则,这是浪费时间试图解决你可能没有的问题的经典问题。

最后一个注意事项......在交叉引用表上(或者像某些人所说的那样连接表),除非ORM要求,否则添加代理键有点愚蠢(在我看来)。

答案 4 :(得分:3)

复合键不仅是复合主键,还包括复合外键。那是什么意思?我的意思是每个引用回原始表的表都需要一个列,用于复合键中的每一列。

这是一个简单的例子,使用通用的学生/班级安排。


名字
姓氏
地址


类名
InstructorFirstName
InstructorLastName
InstructorAddress
MeetingTime

StudentClass - 多对多联接表
StudentFirstName
StudentLastName
StudentAddress
类名
InstructorFirstName
InstructorLastName
InstructorAddress
MeetingTime

你刚从使用代理键的2列多对多表到使用复合键的8列多对多表,因为它们有3列和5列外键。你无法真正摆脱任何这些领域,因为那时记录不会是唯一的,因为学生和教师都可以有重复的名字。哎呀,如果你有两个来自同一地址同名的人,你仍然会遇到严重的麻烦。

答案 5 :(得分:3)

这里给出的大部分答案在我看来并不是由定期使用Access的人提供的,所以我会从那个角度来看(虽然我会重复其他一些已经说过,只有一些特定于访问的评论)。

  1. 只有在没有单列候选键时才使用代理键。这意味着我有代理PK和单列自然PK的表,但没有复合键(连接除外,它们是两个FK的组合,代理或自然无关紧要)。

  2. PK上的Jet / ACE集群,仅限于PK。这有潜在的缺点和潜在的好处(例如,如果您将随机自动编号视为PK)。

  3. 根据我的经验,复合PK的非Null要求使得大多数自然键无法使用可能存在问题的默认值。它同样会破坏您在Jet / ACE中的唯一索引,因此在Access应用程序(2010年之前)中,您最终会在应用程序中强制执行唯一性。从A2010开始,表级数据宏(可以像触发器一样工作)可以用来将该逻辑移动到数据库引擎中。

  4. 复合键可以帮助您避免连接,因为它们会重复使用您必须通过连接从源表获取的代理键的数据。虽然连接可能很昂贵,但主要是外部连接是性能消耗,而且只有非必需的FK才能获得避免外连接的全部好处。但是那么多的数据重复一直困扰着我,因为它似乎违背了我们曾经教过的关于规范化的一切!

  5. 如上所述,我的应用中唯一的复合键位于N:N连接表中。在连接表本身是相关表的父级的相对罕见的情况下,我永远不会将连接表除了添加代理键(例如,Person / Company N:N记录可能具有相关性) JobTitles,即同一公司内的多个工作)。您可以存储代理键,而不是将复合键存储在子表中。我可能不会将代理键设为PK,但是 - 我会将复合PK保留在FK值对上。我只想添加一个带有唯一索引的Autonumber来加入子表。

  6. 我会在想到的时候添加更多。

答案 6 :(得分:1)

使查询和维护变得复杂。如果你真的对这个主题感兴趣,我建议你查看已经涵盖过的帖子数量。这将为您提供比此处任何一个响应更好的信息。

https://stackoverflow.com/search?q=composite+primary+key

答案 7 :(得分:1)

首先,复合键对连接中的性能不利。此外,它们更新记录更糟糕,因为您还必须更新所有子记录。最后,很少有复合键实际上是非常好的键。要成为一把好钥匙,它应该是独一无二的,不能随时改变。您作为所使用的复合键提供的示例未通过两个测试。它不是唯一的(有同一天出生的同名人物)并且名称经常更改导致所有子表的不必要的更新。

对于自动生成密钥重复表的表,这主要是由于以下几个因素:

  • 表格中的其余数据 不能以任何方式识别 独特
  • 忘记的设计失败 创建一个可能的唯一索引 复合键
  • 用户界面设计不佳 哪个不试图找到 匹配记录或允许数据 当下拉可能更多时进入 合适的。

这些都不是代理密钥的错,它们只是表明不称职的开发人员。

答案 8 :(得分:1)

我认为有些程序员会看到复杂性,但却希望避免它,大多数程序员甚至都不会考虑复杂性。

让我们考虑一个具有多个候选键的表的常见示例:Payroll表,其中列为employee_numbersalary_amountstart_date和{{1} }。

四个候选键如下:

end_date

只需要强制执行其中一个密钥,即序列密钥。但是,大多数程序员都不会考虑添加这样的密钥,更不用说知道如何编写密钥了。实际上,我打赌大多数Access编码器会向表中添加一个递增的自动编号列,使自动编号列为UNIQUE (employee_number, start_date); -- simple constraint UNIQUE (employee_number, end_date); -- simple constraint UNIQUE (employee_number, start_date, end_date); -- simple constraint CHECK ( NOT EXISTS ( SELECT Calendar.day_date FROM Calendar, Payroll AS P1 WHERE P1.start_date <= Calendar.day_date AND Calendar.day_date < P1.end_date GROUP BY P1.employee_number, Calendar.day_date ) ); -- sequenced key i.e. no over-lapping periods for the same employee ,无法为任何候选键添加约束,并且会确信自己的表已经有了一把钥匙!