我正在开发一个喜欢自动编号标识符的Access数据库。每个表都使用它们,除了一个,它使用由一个人的名字,姓氏和出生日期组成的密钥。无论如何,人们开始遇到很多重复问题,因为表示关系的表可以保持两次或更多次相同的关系。我决定通过为关系表实现复合键来解决这个问题,因此我没有遇到重复问题。
所以我想知道Access世界中复合键的错误代表是什么?我想编写查询会稍微困难一些,但至少每次输入数据或在前端编辑数据时都不需要进行大量的检查。他们是非常低效还是什么?
答案 0 :(得分:12)
复合键适用于单个表,但是当您开始在表之间创建关系时,它可以获得更多。
考虑两个表Person
和Event
,以及它们之间的多对多关系,称为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`.
另一方面,如果您有Person
和Event
表的自动编号密钥,则只需要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的人提供的,所以我会从那个角度来看(虽然我会重复其他一些已经说过,只有一些特定于访问的评论)。
只有在没有单列候选键时才使用代理键。这意味着我有代理PK和单列自然PK的表,但没有复合键(连接除外,它们是两个FK的组合,代理或自然无关紧要)。
PK上的Jet / ACE集群,仅限于PK。这有潜在的缺点和潜在的好处(例如,如果您将随机自动编号视为PK)。
根据我的经验,复合PK的非Null要求使得大多数自然键无法使用可能存在问题的默认值。它同样会破坏您在Jet / ACE中的唯一索引,因此在Access应用程序(2010年之前)中,您最终会在应用程序中强制执行唯一性。从A2010开始,表级数据宏(可以像触发器一样工作)可以用来将该逻辑移动到数据库引擎中。
复合键可以帮助您避免连接,因为它们会重复使用您必须通过连接从源表获取的代理键的数据。虽然连接可能很昂贵,但主要是外部连接是性能消耗,而且只有非必需的FK才能获得避免外连接的全部好处。但是那么多的数据重复一直困扰着我,因为它似乎违背了我们曾经教过的关于规范化的一切!
如上所述,我的应用中唯一的复合键位于N:N连接表中。在连接表本身是相关表的父级的相对罕见的情况下,我永远不会将连接表除了添加代理键(例如,Person / Company N:N记录可能具有相关性) JobTitles,即同一公司内的多个工作)。您可以存储代理键,而不是将复合键存储在子表中。我可能不会将代理键设为PK,但是 - 我会将复合PK保留在FK值对上。我只想添加一个带有唯一索引的Autonumber来加入子表。
我会在想到的时候添加更多。
答案 6 :(得分:1)
使查询和维护变得复杂。如果你真的对这个主题感兴趣,我建议你查看已经涵盖过的帖子数量。这将为您提供比此处任何一个响应更好的信息。
答案 7 :(得分:1)
首先,复合键对连接中的性能不利。此外,它们更新记录更糟糕,因为您还必须更新所有子记录。最后,很少有复合键实际上是非常好的键。要成为一把好钥匙,它应该是独一无二的,不能随时改变。您作为所使用的复合键提供的示例未通过两个测试。它不是唯一的(有同一天出生的同名人物)并且名称经常更改导致所有子表的不必要的更新。
对于自动生成密钥重复表的表,这主要是由于以下几个因素:
这些都不是代理密钥的错,它们只是表明不称职的开发人员。
答案 8 :(得分:1)
我认为有些程序员会看到复杂性,但却希望避免它,大多数程序员甚至都不会考虑复杂性。
让我们考虑一个具有多个候选键的表的常见示例:Payroll
表,其中列为employee_number
,salary_amount
,start_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
,无法为任何候选键添加约束,并且会确信自己的表已经有了一把钥匙!