主键还是唯一索引?

时间:2009-01-28 11:33:09

标签: sql database database-design

在工作中,我们有一个带有唯一索引而不是主键的大型数据库,一切正常。

我正在为一个新项目设计新数据库,我陷入两难境地:

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

您在项目中使用了什么?

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

15 个答案:

答案 0 :(得分:152)

什么是唯一索引?

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

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,但唯一索引可以为空。
  • 只能有一个主键,但可以有多个唯一索引。
  • 如果没有定义聚簇索引,则主键将是聚簇索引。

答案 1 :(得分:29)

你可以这样看:

主键是唯一的

唯一值不必是元素的表示

含义?;那么一个主键用于识别元素,如果你有一个“人”,你想拥有一个个人识别号码(SSN等),这是你个人的主要信息。

另一方面,此人可能会收到一封独特的电子邮件,但不会识别此人。

我总是有主键,即使在关系表(中间表/连接表)中我也可以拥有它们。为什么?好吧,我喜欢在编码时遵循标准,如果“人”有标识符,汽车有一个标识符,那么,人 - >汽车也应该有一个标识符!

答案 2 :(得分:8)

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

  

FOREIGN KEY约束没有   仅链接到PRIMARY KEY   另一个表中的约束;它可以   也可以定义参考   UNIQUE约束的列   另一张桌子

对于事务复制,您需要主键。来自联机丛书:

  

为事务发布的表   复制必须具有主键。   如果表是事务性的   复制发布,你不能   禁用任何索引   与主键列相关联。   这些索引是必需的   复制。要禁用索引,请执行   必须先从桌子上掉下来   出版物。

这两个答案都适用于SQL Server 2005。

答案 3 :(得分:5)

选择何时使用代理主键而不是自然键是很棘手的。诸如,永远或永远的答案很少有用。我发现这取决于具体情况。

举个例子,我有以下表格:

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有一些关于这一特定主题的精彩文章。

答案 4 :(得分:4)

主键没有缺点。

要向@MrWiggles和@Peter Parker答案添加一些信息,例如当表没有主键时,您将无法在某些应用程序中编辑数据(他们最终会说某些信息无法编辑/删除没有主键的数据)。 Postgresql允许多个NULL值在UNIQUE列中,PRIMARY KEY不允许NULL。另外一些生成代码的ORM可能会对没有主键的表有一些问题。

更新:

据我所知,在MSSQL中无法复制没有主键的表,至少没有问题(details)。

答案 5 :(得分:2)

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

答案 6 :(得分:2)

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

答案 7 :(得分:2)

在关系数据理论中没有关键词,所以你的问题必须在实际层面得到回答。

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

在Oracle中,声明主键将导致代表您创建唯一索引,因此问题几乎没有实际意义。我无法告诉你有关其他DBMS产品的信息。

我赞成声明一个主键。这具有禁止键列中的NULL以及禁止重复的效果。我也赞成声明REFERENCES约束来强制实体完整性。在许多情况下,声明外键的coulmn(s)索引会加速连接。这种索引通常不应该是唯一的。

答案 8 :(得分:1)

除了其他答案所说的内容之外,某些数据库和系统可能要求存在主要内容。一种情况浮现在脑海中;在使用Informix进行企业复制时,必须存在PK才能使表参与复制。

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

CLUSTERED INDEXES与UNIQUE INDEXES有一些缺点。

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

这意味着当你在包含聚簇索引的表上插入或删除很多时,每次(好,差不多,取决于你的填充因子)你更改数据,物理表需要更新以保持排序。

在相对较小的表中,这很好,但是当获得具有GB数据的表,并且插入/删除会影响排序时,您将遇到问题。

答案 11 :(得分:1)

我的理解是主键和具有非空约束的唯一索引是相同的(*);我想根据规范明确指出或暗示的内容(你要表达和明确强制执行的内容)选择一个或另一个。如果它需要唯一性而不是null,那么将它作为主键。如果只是发生了唯一索引的所有部分都不为null而没有任何要求,那么只需将其作为唯一索引。

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

(*)除了实际差异之外:主键可以是某些操作的默认唯一键,例如定义外键。防爆。如果定义引用表的外键并且不提供列名,如果引用的表具有主键,则主键将是引用的列。否则,必须明确命名引用的列。

其他人在这里提到了数据库复制,但我不知道。

答案 12 :(得分:0)

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

答案 13 :(得分:0)

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

答案 14 :(得分:-1)

如果由我决定......

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

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

然后,您将向表中添加至少一个其他唯一索引以供应用程序使用。这将是employee_id,account_id或customer_id等的索引。如果可能,此索引不应是复合索引。

我赞成在复合索引上单独使用几个字段的索引。只要where子句包含这些字段,数据库就会使用单个字段索引,但只有当您以完全正确的顺序提供字段时才会使用复合 - 这意味着它不能在复合索引中使用第二个字段,除非你在where子句中提供了第一个和第二个。

我全都使用计算或函数类型索引 - 并建议在复合索引上使用它们。通过在where子句中使用相同的函数,可以很容易地使用函数索引。

这可以满足您的应用要求。

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