关于sql:主键还是唯一索引?

Primary key or Unique index?

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

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

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

你在项目中使用了什么?

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


什么是独特的指数?

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

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


你可以这样看:

主键是唯一的

唯一值不必是元素的表示

含义?;好的主键用于标识元素,如果您有"人",您希望拥有一个个人识别号码(SSN或类似物),这是您的主要人物。

另一方面,该人可能有一个独特的电子邮件,但不会识别该人。

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


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

A FOREIGN KEY constraint does not have
to be linked only to a PRIMARY KEY
constraint in another table; it can
also be defined to reference the
columns of a UNIQUE constraint in
another table

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

Tables published for transactional
replication must have a primary key.
If a table is in a transactional
replication publication, you cannot
disable any indexes that are
associated with primary key columns.
These indexes are required by
replication. To disable an index, you
must first drop the table from the
publication.

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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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有一些关于这个特定主题的精彩文章。


主键没有缺点。

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

更新:

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


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

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

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

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


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


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


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

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

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

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


CLUSTERED INDEXES与UNIQUE INDEXES有一些缺点。

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

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

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


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

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


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


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


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


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

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

将自动递增整数或长id列添加到每个表以用作主键可以满足数据库要求。

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

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

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

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

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