13

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

所以我想知道 Access 世界中复合键的不良代表是怎么回事?我想编写查询稍微困难一些,但至少您不必每次在前端输入甚至编辑数据时都进行大量检查。他们是令人难以置信的超级低效还是什么?

4

9 回答 9

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`.

另一方面,如果您对PersonandEvent表有自动编号的键,则只需要Appointment表中的两个字段来识别关系,并且条件要小得多:

select Person,*, Event.*
from Person, Event, Appointment
where
  Person.Id = Appointment.PersonId and Event.Id = Appointment.EventId
于 2010-08-18T14:09:22.060 回答
7

如果您只使用纯自写SQL来访问您的数据,它们是可以的。

但是,一些ORMs、适配器等需要有一个PK字段来标识一条记录。

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

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

使用自然键时,应确保它们本质上是唯一且不可变的,即一个实体总是由相同的键值标识,一旦被模型反映,任何值只能标识一个实体。

在您的情况下,情况并非如此。

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

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

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

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

于 2010-08-18T14:04:00.853 回答
4

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

我经常推测,完全弃用和逐步淘汰“主键”一词的使用将是一件好事。这样做会使数据库设计者的注意力集中在真正的问题上:一个表应该有尽可能多的键来确保数据的正确性,并且其中一些键可能是复合的。废除主键概念将消除所有关于主键应该是什么的愚蠢辩论。

于 2010-08-18T18:49:04.153 回答
3

在我看来,这里给出的大多数答案似乎不是由定期使用 Access 的人给出的,所以我会从这个角度加入(尽管我会重复其他一些人所说的话,只是一些特定于 Access 的评论)。

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

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

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

  4. 复合键可以帮助您避免连接,因为它们重复使用代理键的数据,您必须通过连接从源表中获取这些数据。虽然连接可能很昂贵,但主要是外连接会消耗性能,并且只有使用非必需的 FK 才能获得避免外连接的全部好处。但是如此多的数据重复一直困扰着我,因为它似乎违背了我们所学过的关于标准化的所有内容!

  5. 正如我上面提到的,我的应用程序中唯一的复合键位于 N:N 连接表中。我永远不会将代理键添加到连接表,除非在连接表本身是相关表的父级的相对罕见的情况下(例如,Person/Company N:N 记录可能具有相关的 JobTitles,即,其中的多个工作同一家公司)。与其将复合键存储在子表中,不如存储代理键。不过,我可能不会将代理键设为 PK——我会将复合 PK 保留在一对 FK 值上。我只想添加一个带有唯一索引的自动编号,用于加入子表。

我会根据我的想法添加更多。

于 2010-08-18T23:45:47.047 回答
3

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

单个“id”列(或代理键)的优点是它可以通过制作更窄的键来提高性能。因为这个键可能被携带到该表上的索引(作为从索引行返回物理行的指针)和其他表作为可以减少空间和提高性能的 FK 列。不过,这在很大程度上取决于您的 RDBMS 的特定架构。不幸的是,我对 Access 不够熟悉,无法对此发表评论。

正如 Quassnoi 指出的那样,一些 ORM(以及其他第三方应用程序、ETL 解决方案等)没有处理复合键的能力。不过,除了一些 ORM 之外,最新的第三方应用程序将支持复合键。不过,ORM 通常在采用它方面要慢一些。

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

另一个问题是自动递增 ID 通常不可移植。当然,您可以在系统之间移动它们,但由于它们在现实世界中没有实际基础,因此不可能在考虑到实体的其他所有信息的情况下确定一个。这在 ETL 中变得很重要。

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

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

最后一点……在交叉引用表(或某些人称之为连接表)上,除非 ORM 需要,否则添加代理键有点愚蠢(在我看来)。

于 2010-08-18T14:19:33.430 回答
3

复合键不仅是复合主键,也是复合外键。我的意思是什么?我的意思是,每个引用原始表的表都需要为复合键中的每一列提供一个列。

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

人名 姓氏 地址
_

Class
ClassName
InstructorFirstName
InstructorLastName
InstructorAddress
MeetingTime

StudentClass - 多对多连接表
StudentFirstName
StudentLastName
StudentAddress
ClassName
InstructorFirstName
InstructorLastName
InstructorAddress
MeetingTime

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

于 2010-08-18T14:20:30.850 回答
1

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

至于带有自动生成键的表导致重复,这主要是由于几个因素:

  • 表中的其余数据无法以任何方式识别为唯一的
  • 忘记在可能的复合键上创建唯一索引的设计失败
  • 用户界面设计不佳,不会尝试找到匹配的记录,或者在下拉时允许数据输入可能更合适。

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

于 2010-08-18T14:20:34.280 回答
1

我认为一些编码人员看到了复杂性但想避免它,大多数编码人员甚至根本不考虑寻找复杂性。

让我们考虑一个具有多个候选键的表的常见示例:具有、和Payroll列的表。employee_numbersalary_amountstart_dateend_date

四个候选键如下:

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

只需要强制执行这些密钥中的一个,即已排序的密钥。但是,大多数编码人员不会考虑添加这样的密钥,更不用说一开始就知道如何对其进行编码。事实上,我敢打赌,大多数 Access 编码员会在表中添加一个递增的自动编号列,将自动编号列PRIMARY KEY设为

于 2010-09-07T08:31:52.673 回答
1

它使查询和维护复杂化。如果您真的对这个主题感兴趣,我建议您查看已经涵盖此主题的帖子数量。这将为您提供比这里的任何回复更好的信息。

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

于 2010-08-18T14:01:52.297 回答