主键还是唯一索引? ?

在工作中,我们有一个大型的数据库,其中有唯一的索引,而不是主键,所有的工作都很好。

我正在为一个新项目设计新的数据库,我有一个困境:

在 DB 理论中,主键是基本元素,这没问题,但是在 REAL 项目中,这两者的优缺点是什么?

你在项目中使用什么?

编辑: ... MS SQL 服务器上的主键和复制怎么样?

164865 次浏览

你可以这样看:

主键是唯一的

Unique 值不一定是元素的表示形式

什么意思?一个主键用来标识元素,如果你有一个“ Person”,你希望有一个 PIN码(SSN 或类似的) ,它是你 Person 的主键。

另一方面,这个人可能有一封独一无二的电子邮件,但是不能识别这个人。

我总是有主键,即使在关系表(中间的表/连接表)中也可能有。为什么?我喜欢在编码时遵循一个标准,如果“ Person”有一个标识符,Car 有一个标识符,那么 Person-> Car 也应该有一个标识符!

如果某个内容是主键,则根据数据库引擎,整个表将按主键进行排序。这意味着主键上的查找要快得多,因为它不必像对待其他类型的索引那样进行任何解引用。除此之外,这只是理论。

除了其他答案所说的,一些数据库和系统可能 要求的主要目前。我想到了一种情况; 当使用 Informix 进行企业复制时,必须为参与复制的表提供一个 PK。

只要你不允许一个值为 NULL,它们应该被处理相同,但是值 NULL 在数据库上的处理是不同的(AFAIK MS-SQL 不允许多于一个(1) NULL 值,mySQL 和 Oracle 允许这样,如果一个列是 UNIQUE) 因此,您可以定义这个列 NOTNULL UNIQUE INDEX

主键没有缺点。

为了给@MrWiggles 和@Peter Parker 添加一些信息,例如,当 table 没有主键时,你将无法在某些应用程序中编辑数据(他们最终会说没有主键就无法编辑/删除数据之类的话)。Postgreql 允许 UNIQUE 列中有多个 NULL 值,PRIMARYKEY 不允许 NULL 值。另外,一些生成代码的 ORM 可能会在没有主键的表中出现一些问题。

更新:

据我所知,在 MSSQL 不可能复制没有主键的表,至少不会出现问题(详情)。

外键使用唯一的约束和主键。来自联机丛书:

FOREIGNKEY 约束不具有 只能链接到主密钥 约束放在另一个表中; 它可以 也被定义为引用 中的 UNIQUE 约束的列 另一张桌子

对于事务复制,您需要主键:

为事务发布的表 复制必须具有主键。 如果表位于事务中,则为 复制出版物,则不能 禁用所有索引 与主键列关联的。 这些索引是 若要禁用索引,请使用 必须首先将表从 出版物。

两个答案都适用于 SQLServer2005。

在关系数据理论中没有主键这样的东西,所以您的问题必须在实践层面上得到回答。

唯一索引不是 SQL 标准的一部分。DBMS 的特定实现将决定声明唯一索引的后果。

在 Oracle 中,声明一个主键将导致为您创建一个唯一的索引,因此这个问题几乎没有实际意义。我不能告诉您关于其他 DBMS 产品的信息。

我赞成声明一个主键。这样做的结果是禁止键列中的 NULL,以及禁止重复。我还赞成声明 REFERENCES 约束来强制执行实体完整性。在许多情况下,对外键的列声明索引将加速连接。这种索引通常不应该是唯一的。

我几乎从不创建没有数字主键的表。如果还有一个自然键应该是唯一的,那么我还在它上面添加了一个唯一索引。联接在整数上比多列自然键更快,数据只需要在一个地方更改(自然键往往需要更新,这在主键-外键关系中是一件坏事)。如果需要复制,请使用 GUID 而不是整数,但在大多数情况下,我更喜欢用户可读的密钥,特别是如果他们需要看到它来区分 John Smith 和 John Smith。

我不创建代理键的少数几次是当我有一个涉及多对多关系的联接表时。在这种情况下,我将这两个字段都声明为主键。

选择何时使用代理主键而不使用自然键是一个棘手的问题。诸如“永远”或“永远”之类的答案很少有用。我发现这取决于情况。

例如,我有以下表格:

CREATE TABLE toll_booths (
id            INTEGER       NOT NULL PRIMARY KEY,
name          VARCHAR(255)  NOT NULL,
...
UNIQUE(name)
)


CREATE TABLE cars (
vin           VARCHAR(17)   NOT NULL PRIMARY KEY,
license_plate VARCHAR(10)   NOT NULL,
...
UNIQUE(license_plate)
)


CREATE TABLE drive_through (
id            INTEGER       NOT NULL PRIMARY KEY,
toll_booth_id INTEGER       NOT NULL REFERENCES toll_booths(id),
vin           VARCHAR(17)   NOT NULL REFERENCES cars(vin),
at            TIMESTAMP     DEFAULT CURRENT_TIMESTAMP NOT NULL,
amount        NUMERIC(10,4) NOT NULL,
...
UNIQUE(toll_booth_id, vin)
)

我们有两个实体表(toll_boothscars)和一个事务表(drive_through)。toll_booth表使用代理键,因为它没有保证不会更改的自然属性(名称可以很容易地更改)。cars表使用一个自然的主键,因为它有一个不变的唯一标识符(vin)。drive_through事务表使用了一个代理键以便于识别,但是对于在插入记录时保证唯一的属性也有一个唯一的约束。

Http://database-programmer.blogspot.com 有一些关于这个特殊主题的很好的文章。

什么是唯一索引?

列上的唯一索引是该列上的索引,该索引还强制执行约束,即该列中不能有两个相等的值在两个不同的行中。例如:

CREATE TABLE table1 (foo int, bar int);
CREATE UNIQUE INDEX ux_table1_foo ON table1(foo);  -- Create unique index on foo.


INSERT INTO table1 (foo, bar) VALUES (1, 2); -- OK
INSERT INTO table1 (foo, bar) VALUES (2, 2); -- OK
INSERT INTO table1 (foo, bar) VALUES (3, 1); -- OK
INSERT INTO table1 (foo, bar) VALUES (1, 4); -- Fails!


Duplicate entry '1' for key 'ux_table1_foo'

最后一次插入失败,因为它在第二次尝试将值1插入该列时违反了列 foo上的唯一索引。

在 MySQL 中,一个唯一的约束允许多个 NULL。

可以对多个列创建唯一的索引。

主键对唯一索引

事情都是一样的:

  • 主键意味着唯一索引。

与众不同的东西:

  • 主键也暗示为 NOT NULL,但惟一索引可以为空。
  • 只能有一个主键,但可以有多个唯一索引。
  • 如果没有定义聚集索引,那么主键将是聚集索引。

集群索引与唯一索引相比有一些缺点。

如前所述,CLUSTERED INDEX 对表中的数据进行物理排序。

这意味着,如果在一个包含聚集索引的表上进行了大量的插入或删除操作,那么每次(嗯,几乎,取决于填充因子)更改数据时,都需要更新物理表以保持排序。

在相对较小的表中,这样做是可以的,但是当访问具有 GB 值的数据的表时,插入/删除操作会影响排序,您将遇到问题。

唯一索引可以有一个 NULL 值。它创建非聚集索引。 主键不能包含 NULL 值。它创建 CLUSTERED INDEX。

我的理解是,一个主键和一个带有非空约束的唯一索引是相同的(*) ; 我假设其中一个选择一个或另一个取决于规范明确表示或暗示的内容(一个你想要表达和明确强制执行的问题)。如果它需要唯一性和非空,那么将其设置为主键。如果恰巧一个唯一索引的所有部分都是非空的,而且没有任何这方面的要求,那么就将它设置为唯一索引。

唯一的区别是,您可能有多个非空唯一索引,而不能有多个主键。

(*)除了一个实际的区别: 主键可以是某些操作的默认唯一键,比如定义外键。前女友。如果定义了引用表的外键,但没有提供列名,那么如果引用的表有主键,那么主键就是引用的列。否则,引用的列必须显式命名。

这里的其他人提到了 DB 复制,但是我不知道。

在 MSSQL,为了获得聚集索引的最佳性能,主键应该单调地增加。因此,具有标识插入的整数优于可能不是单调增长的任何自然键。

如果由我决定..。

您需要满足数据库和应用程序的需求。

在每个表中添加一个自动递增的整数列或长 id 列作为主键,以满足数据库需求。

然后向表中添加至少一个其他唯一索引,以供应用程序使用。这将是针对 EmployeID、 account _ id 或 customer _ id 等的索引。如果可能的话,这个索引不应该是一个复合索引。

相对于复合索引,我更倾向于在几个字段上单独使用索引。只要 where 子句包含这些字段,数据库就会使用单个字段索引,但是只有当你以正确的顺序提供字段时,数据库才会使用组合字段——这意味着它不能在组合索引中使用第二个字段,除非你在 where 子句中同时提供第一个和第二个字段。

我完全赞成使用计算索引或函数类型索引,并建议在使用复合索引时使用它们。通过在 where 子句中使用相同的函数,可以非常容易地使用函数索引。

这将满足您的应用程序需求。

其他非主索引很可能实际上是该索引键值到主键值的映射,而不是 rowid ()的。这允许进行物理排序操作和删除操作,而不必重新创建这些索引。