根据我对文档的理解,以下定义是等价的:

create table foo (
    id serial primary key,
    code integer,
    label text,
    constraint foo_uq unique (code, label));

create table foo (
    id serial primary key,
    code integer,
    label text);
create unique index foo_idx on foo using btree (code, label);    

然而,Postgres 9.4手册中的注释说:

向表中添加唯一约束的首选方法是ALTER table…添加约束。使用索引来强制惟一的约束 可以被认为是一个不应该被认为是实现细节的实现吗 直接访问。

(编辑:此说明已从Postgres 9.5的手册中删除。)

仅仅是风格的问题吗?选择其中一个变量的实际后果是什么(例如在性能方面)?


当前回答

我对这个基本但重要的问题有些怀疑,所以我决定以身作则。

让我们创建带有两个列的测试表master,带有唯一约束的con_id和带有唯一索引的ind_id。

create table master (
    con_id integer unique,
    ind_id integer
);
create unique index master_unique_idx on master (ind_id);

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_unique_idx" UNIQUE, btree (ind_id)

在表描述(psql中的\d)中,你可以区分唯一的约束和唯一的索引。

独特性

以防万一,我们来检查一下唯一性。

test=# insert into master values (0, 0);
INSERT 0 1
test=# insert into master values (0, 1);
ERROR:  duplicate key value violates unique constraint "master_con_id_key"
DETAIL:  Key (con_id)=(0) already exists.
test=# insert into master values (1, 0);
ERROR:  duplicate key value violates unique constraint "master_unique_idx"
DETAIL:  Key (ind_id)=(0) already exists.
test=#

它像预期的那样工作!

外键

现在,我们将用两个外键来定义详细表,引用master中的两列。

create table detail (
    con_id integer,
    ind_id integer,
    constraint detail_fk1 foreign key (con_id) references master(con_id),
    constraint detail_fk2 foreign key (ind_id) references master(ind_id)
);

    Table "public.detail"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Foreign-key constraints:
    "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

没有错误。让我们确保它能工作。

test=# insert into detail values (0, 0);
INSERT 0 1
test=# insert into detail values (1, 0);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk1"
DETAIL:  Key (con_id)=(1) is not present in table "master".
test=# insert into detail values (0, 1);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk2"
DETAIL:  Key (ind_id)=(1) is not present in table "master".
test=#

两个列都可以在外键中引用。

使用索引约束

可以使用现有的唯一索引添加表约束。

alter table master add constraint master_ind_id_key unique using index master_unique_idx;

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_ind_id_key" UNIQUE CONSTRAINT, btree (ind_id)
Referenced by:
    TABLE "detail" CONSTRAINT "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    TABLE "detail" CONSTRAINT "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

现在列约束描述没有区别了。

部分指标

在表约束声明中不能创建部分索引。 它直接来自创建表....的定义 在唯一索引声明中,可以设置WHERE子句来创建部分索引。 您还可以在表达式上创建索引(不仅仅是在列上),并定义一些其他参数(排序规则、排序顺序、空值位置)。

不能使用部分索引添加表约束。

alter table master add column part_id integer;
create unique index master_partial_idx on master (part_id) where part_id is not null;

alter table master add constraint master_part_id_key unique using index master_partial_idx;
ERROR:  "master_partial_idx" is a partial index
LINE 1: alter table master add constraint master_part_id_key unique ...
                               ^
DETAIL:  Cannot create a primary key or unique constraint using such an index.

其他回答

锁是有区别的。 添加索引不会阻塞对表的读访问。 添加一个约束确实会放置一个表锁(所以所有的选择都被阻塞),因为它是通过ALTER table添加的。

有一件非常小的事情,只能使用约束而不能使用索引,那就是使用ON冲突ON约束子句(另见此问题)。

这行不通:

CREATE TABLE T (a INT PRIMARY KEY, b INT, c INT);
CREATE UNIQUE INDEX u ON t(b);

INSERT INTO T (a, b, c)
VALUES (1, 2, 3)
ON CONFLICT ON CONSTRAINT u
DO UPDATE SET c = 4
RETURNING *;

它产生:

[42704]: ERROR: constraint "u" for table "t" does not exist

将索引转换为约束:

DROP INDEX u;
ALTER TABLE t ADD CONSTRAINT u UNIQUE (b);

INSERT语句现在可以工作了。

使用UNIQUE INDEX与UNIQUE CONSTRAINT的另一个优点是,您可以轻松地并发地删除/创建索引,而使用CONSTRAINT则不行。

唯一性是一个限制。它恰好是通过创建来实现的 唯一的索引,因为一个索引能够很快地搜索所有现有的 值,以确定给定值是否已经存在。 从概念上讲,索引是一个实现细节,惟一性也应该是 只与约束相关联。

全文

所以速度性能应该是一样的

我遇到的另一件事是,您可以在唯一索引中使用sql表达式,但不能在约束中使用。

所以,这是行不通的:

CREATE TABLE users (
    name text,
    UNIQUE (lower(name))
);

但下面的做法是可行的。

CREATE TABLE users (
    name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));