主键与唯一约束?

时间:2020-03-06 14:59:02  来源:igfitidea点击:

我目前正在设计一个全新的数据库。在学校里,我们总是学会在每张桌子上放一个主键。

我读了很多文章/讨论/新闻组的帖子,说最好使用唯一约束(某些数据库也称为唯一索引)而不是PK。

观点是什么?

解决方案

我们可以提供对这些文章的引用吗?

我认为没有理由更改已尝试和真实的方法。毕竟,主键是关系数据库的基本设计功能。

使用UNIQUE达到相同的目的听起来对我来说实在是小菜一碟。他们的理由是什么?

编辑:我的注意力刚刚回到了这个旧答案。也许我们阅读的有关PK与UNIQUE的讨论涉及的是人们将某件事称为PK,其唯一目的是在其上增强唯一性。答案是,如果它是密钥,则使其成为密钥,否则使其成为唯一。

极少数的非规范化会使我们想要一个没有主键的表。主键仅凭其作为PK的性质就自动具有唯一性约束。

当我们要保证主键的ADDITION列中的唯一性时,将使用唯一性约束。

始终拥有PK的规则是一个好规则。

http://msdn.microsoft.com/en-us/library/ms191166.aspx

在要从该表建立到将引用该值的其他表的关系的情况下,应使用主键。但是,根据表的性质以及我们要对其应用唯一性约束的数据,我们可以将特定字段用作自然主键,而不必建立替代键。当然,代理与自然键是另外一回事。 :)

如果此表与其他表之间没有建立关系,则可以使用唯一键。例如,一个包含有效电子邮件地址列表的表,将在插入新用户记录或者类似记录之前将其与之进行比较。或者,当我们在具有主键但也必须绝对唯一的表中具有值时,可以使用唯一键。例如,如果我们有一个包含用户名的用户表。我们不想将用户名用作主键,但它也必须是唯一的,才能用于登录目的。

问题是主键可以是一个或者多个列,这些列唯一地标识表的单个记录,其中"唯一约束"只是对字段的约束,该字段仅允许表中任何给定数据元素的单个实例。

我个人使用GUID或者自动递增BIGINTS(SQL SERVER的身份插入)获得用于表之间交叉引用的唯一键。然后,我将使用其他数据来允许用户选择特定的记录。

例如,我将有一个雇员列表,并且在幕后使用的每条记录都有一个GUID,但是当用户选择一名雇员时,他们将基于以下字段来选择他们:LastName + FirstName +员工编号。

在这种情况下,我的主键是LastName + FirstName + EmployeeNumber,而唯一键是关联的GUID。

主键只是为特殊处理(自动创建索引等)而挑选出来的候选键(唯一约束)。

我希望反对他们的人认为没有理由将一把钥匙与另一把钥匙区别对待。那就是我的立场。

[编辑]显然,即使没有50分,我也无法对自己的答案发表评论。

@chris:我认为没有任何危害。 "主键"实际上只是语法糖。我一直都在使用它们,但是我当然不认为它们是必需的。唯一键是必需的,是的,但不一定是主键。

如果我们打算使用LINQ-to-SQL,那么如果我们打算执行更新,则表将需​​要主键;如果我们打算在断开连接的环境中工作(例如,通过WCF服务应用程序)。

如果我们喜欢.NET,则PK和FK是朋友。

主键实际上只是不允许NULL的候选键。因此,用SQL术语来说,它与任何其他唯一键没有什么不同。

但是,对于我们的非理论RDBMS,我们应该有一个主键,但我从未听说过它另有说法。如果该主键是代理键,那么我们还应该对自然键有唯一的约束。

重要的一点是,我们应该对所有候选键(无论是自然键还是替代键)都具有唯一的约束。然后,我们应该选择一个在外键中最容易引用的键作为主键*。

我们还应该具有聚集索引*。这可以是主键,也可以是自然键,但也不一定要是主键。我们应该根据表的查询用法选择聚簇索引。如有疑问,主键不是一个坏的首选。

  • 尽管从技术上讲,仅需要在外键关系中引用唯一键,但是公认的惯例是非常喜欢主键。实际上,如果某些RDBMS仅允许主键引用,我不会感到惊讶。
  • 编辑:已经指出,Oracle的"聚集表"和"聚集索引"术语与Sql Server不同。我在Oracle-ese中所说的相当于索引排序表,推荐用于OLTP表-我认为这将是SO问题的主要焦点。我假设如果我们负责大型OLAP数据仓库,那么我们应该已经对数据库设计和优化有自己的见解。

除非表是在处理数据时暂存数据的临时表,否则我们始终想在表上放一个主键,这就是为什么:

1唯一约束可以允许空值,但是主键永远不允许空值。如果我们对具有空值的列进行联接查询,则可以从结果数据集中消除那些行,因为空值不等于空值。这就是甚至大公司也可能会犯会计错误并不得不重述其利润的方式。他们的查询未显示应包含在总数中的某些行,因为其唯一索引的某些列中存在空值。 Shoulda使用了主键。

2唯一索引将自动放置在主键上,因此我们不必创建一个。

3个大多数数据库引擎将自动在主键上放置聚簇索引,从而使查询速度更快,因为行连续存储在数据块中。 (如果这样做可以加快查询的速度,可以将其更改为将聚簇索引放置在不同的索引上。)如果表没有聚簇索引,则行将不会连续存储在数据块中,从而导致查询速度较慢,因为读/写头必须遍历整个磁盘才能拾取数据。

4许多前端开发环境需要主键才能更新表或者进行删除。

在这里,我们需要在逻辑构造和物理构造之间以及在理论和实践之间进行区分。

首先,从理论上讲,如果我们没有主键,那么就没有表。就这么简单。因此,问题不是表是否应该具有主键(当然应该具有主键),而是如何在RDBMS中对其进行标记。

在物理级别,大多数RDBMS将主键约束实现为唯一索引。如果我们选择的RDBMS是其中之一,则在将列指定为主键与仅对列施加唯一约束之间可能没有太大的实际区别。但是:这些选项之一可以抓住意图,而另一个则不能。因此,这一决定是不费吹灰之力的。

此外,如果适当地标记了主键,则某些RDBMS会提供其他功能,例如图表和半自动外键约束支持。

任何告诉我们使用"唯一约束"而不是"主键"作为一般规则的人,都应该提供一个非常好的理由。

posts saying that it's better to use unique constraint (aka unique index for some db) instead of PK

我猜这里唯一的要点是相同的旧讨论"自然键与代理键",因为唯一索引和pk是同一件事。

翻译:

帖子说最好使用自然键而不是代理键

我们应该始终有一个主键。

但是我怀疑问题只是措辞有点误导,我们实际上是在询问主键应该始终是自动生成的数字(也称为代理键)还是某些唯一的字段,而该字段是实际有意义的数据(也称为自然)。键),例如用于人的SSN,用于书本的ISBN等。

这个问题是DB领域的古老宗教战争。

我的看法是,如果自然键确实是唯一的且永不更改,则它们是更可取的。但是,我们应该小心,即使在某些情况下,甚至看起来像个人SSN的稳定物品也可能会发生变化。

我认为你们可能都需要。本质上,主键必须是唯一的并且不能为空。它们通常是替代键,因为整数创建的连接比字符字段(尤其是多个字段字符连接)快。但是,由于这些通常是自动生成的,因此它们不能保证数据记录的唯一性(不包括id本身)。如果表具有一个唯一的自然键,则应该在其上具有唯一索引以防止重复数据输入。这是基本的数据完整性要求。

编辑添加:现实世界的数据通常不具有真正保证规范化表结构中唯一性的自然键,这也是一个现实问题,尤其是在数据库以人为中心的情况下。姓名,甚至姓名,地址和电话号码的组合(在相同的医学实践中认为父子)不一定是唯一的。

我通常同时使用PK和UNIQUE KEY。因为即使我们不在架构中表示PK,也始终会在内部为我们生成一个。对于SQL Server 2005和MySQL 5都是如此。

但是我不在SQL中使用PK列。它用于管理目的,例如删除一些错误的行,如果将PK值设置为AUTO INCREMENT,则找出PK值之间的间隙。并且,将PK作为数字而不是一组列或者char数组是有意义的。

关于这个主题,我已经写了很多文章:如果我们阅读了我的任何文章,请明确一点,我可能专门指的是Jet a.k.a. MS Access。

在Jet中,使用非维护的聚簇索引(在紧凑型上聚簇)在主键上对表进行物理排序。如果该表没有PK,但确实在NOT NULL列上使用UNIQUE约束定义了候选键,则引擎将为聚集索引选择一个(如果表没有聚集索引,则该堆称为堆,可以说根本不是表) !)引擎如何选择候选钥匙?它可以选择一个包含可空列的列吗?我真的不知道关键是,在Jet中,指定引擎的聚集索引的唯一显式方法是使用PRIMARY KEY。当然,Jet中的PK还有其他用途,例如如果在SQL DDL中的FOREIGN KEY声明中省略了一个,它将用作键,但又为什么不明确。

Jet的麻烦在于,大多数创建表的人都不了解或者不关心聚簇索引。实际上,大多数用户(我押注)在每个表上都放置了一个autoincrement Autonumber列,并仅在此列上定义了PRIMARY KEY,而没有对自然键和候选键施加任何唯一的约束(无论autoincrement列是否可以视为密钥而不暴露给最终用户本身就是另外一个讨论)。在这里,我将不详细介绍聚簇索引,但足以说IMO唯一的自动增量列很少是理想的选择。

无论我们使用哪种SQL引擎,PRIMARY KEY的选择都是任意的,并且取决于引擎。通常,引擎会对PK应用特殊含义,因此我们应该找出它是什么,并利用它来发挥自己的优势。我鼓励人们使用NOT NULL UNIQUE约束,希望他们会更多地考虑所有候选键,尤其是当他们选择使用(应该)在数据模型中没有意义的"自动编号"列时。但是我宁愿人们选择一个经过深思熟虑的密钥并使用PRIMARY KEY,而不是出于习惯而不将其放在自动增量列上。

所有的桌子都应该有PK吗?我之所以说是的,是因为这样做否则至少意味着我们会因为引擎提供PK而略微失去优势,最糟糕的是我们没有数据完整性。

BTW Chris OC在这里很好地介绍了时态表,这些表需要排序的主键(小写),而这些键不能通过简单的PRIMARY KEY约束(大写的SQL关键字)来实现。