Sql 为什么我读到这么多关于使用复合键的负面意见?

Sql 为什么我读到这么多关于使用复合键的负面意见?,sql,ms-access,primary-key,composite-key,composite-primary-key,Sql,Ms Access,Primary Key,Composite Key,Composite Primary Key,我正在开发一个Access数据库,它喜欢自动编号的标识符。每个表都使用它们,只有一个表使用由一个人的名字、姓氏和生日组成的键。不管怎么说,人们开始在使用重复项时遇到很多问题,因为表示关系的表可能会持有相同的关系两次或更多次。我决定通过为关系表实现复合键来解决这个问题,从那以后我就再也没有遇到过重复键的问题 所以我想知道复合密钥在访问世界中的坏名声是怎么回事?我想写一个查询要稍微困难一些,但至少你不必每次在前端输入甚至编辑数据时都进行大量的检查。他们是超级低效还是什么 它使查询和维护变得复杂。如果

我正在开发一个Access数据库,它喜欢自动编号的标识符。每个表都使用它们,只有一个表使用由一个人的名字、姓氏和生日组成的键。不管怎么说,人们开始在使用重复项时遇到很多问题,因为表示关系的表可能会持有相同的关系两次或更多次。我决定通过为关系表实现复合键来解决这个问题,从那以后我就再也没有遇到过重复键的问题


所以我想知道复合密钥在访问世界中的坏名声是怎么回事?我想写一个查询要稍微困难一些,但至少你不必每次在前端输入甚至编辑数据时都进行大量的检查。他们是超级低效还是什么

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


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

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

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

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

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

你的情况并非如此

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

其次,我很容易想象两个约翰·史密斯在同一天出生

前者意味着,如果一个人更改了姓名,您必须在每个涉及
人的表中更新姓名;后者意味着第二个
John Smith
将无法进入您的数据库


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

复合键对于单个表来说是很好的,但是当你开始创建表之间的关系时,它会有点大。

考虑两个表
Person
Event
,它们之间的多对多关系称为
约会

如果在
Person
表中有一个由名字、姓氏和出生日期组成的复合键,在
Event
表中有一个由地点和姓名组成的复合键,则在
约会
表中会有五个字段来标识关系

约束关系的条件将非常长:

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`.
另一方面,如果您为
人员
事件
表设置了自动编号键,则在
约会
表中只需两个字段即可识别关系,条件要小得多:

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

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

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

正如Quassnoi所指出的,一些ORM(以及其他第三方应用程序、ETL解决方案等)没有处理复合密钥的能力。不过,除了一些ORMs之外,最新的第三方应用程序都支持复合密钥。不过,一般来说,ORMs在采用这一点上有点慢

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

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

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

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


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

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

下面是一个简单的例子,使用一般的学生/班级安排


名字
姓氏
地址

类别
类名
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