Postgres唯一约束vs索引

Ada*_*ski 134 sql postgresql unique

据我所知,文档中的以下定义是等效的:

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);    
Run Code Online (Sandbox Code Playgroud)

但是,您可以在注释中阅读:向表中添加唯一约束的首选方法是ALTER TABLE ... ADD CONSTRAINT.使用索引来强制执行唯一约束可以被视为不应直接访问的实现细节.

这只是一个好风格的问题吗?选择其中一种变体(例如性能)的实际后果是什么?

kli*_*lin 119

我对这个基本但重要的问题有些怀疑,所以我决定通过实例学习.

让我们创建具有两列的测试表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)
Run Code Online (Sandbox Code Playgroud)

在表描述中(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=#
Run Code Online (Sandbox Code Playgroud)

它按预期工作!

外键

现在我们将定义详细信息表,其中两个外键引用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)
Run Code Online (Sandbox Code Playgroud)

好吧,没有错误.让我们确保它有效.

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=#
Run Code Online (Sandbox Code Playgroud)

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

约束使用索引

您可以使用现有唯一索引添加表约束.

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)
Run Code Online (Sandbox Code Playgroud)

现在列约束描述之间没有区别.

部分索引

在表约束声明中,您无法创建部分索引.它直接由来自定义create table ....在唯一索引声明中,您可以设置WHERE clause为创建部分索引.您还可以在表达式上创建索引(不仅在列上)并定义一些其他参数(排序规则,排序顺序,NULL放置).

您无法使用部分索引添加表约束.

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.
Run Code Online (Sandbox Code Playgroud)


小智 27

使用UNIQUE INDEXvs.的另一个优点UNIQUE CONSTRAINT是,您可以轻松地DROP/ CREATE索引CONCURRENTLY,而使用约束则不能.

  • AFAIK不可能同时删除唯一索引.https://www.postgresql.org/docs/9.3/static/sql-dropindex.html"使用此选项时需要注意几个注意事项.只能指定一个索引名称,并且不支持CASCADE选项.(因此,不能以这种方式删除支持UNIQUE或PRIMARY KEY约束的索引.)" (3认同)

Eug*_*kov 10

唯一性是一种约束.它恰好通过创建唯一索引来实现,因为索引可以快速搜索所有现有值,以确定给定值是否已存在.

从概念上讲,索引是一个实现细节,唯一性应该只与约束相关联.

全文

所以速度表现应该是一样的


Mas*_*inn 8

由于很多人都提供了唯一索引优于唯一约束的优点,这里有一个缺点:唯一约束可以延迟(仅在事务结束时检查),唯一索引不能。

  • 由于索引没有用于延迟的 API,只有约束有,因此虽然延迟机制在幕后支持唯一约束,但无法将索引声明为可延迟或延迟它。 (2认同)

김민준*_*김민준 6

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

所以,这不起作用:

CREATE TABLE users (
    name text,
    UNIQUE (lower(name))
);
Run Code Online (Sandbox Code Playgroud)

但以下作品。

CREATE TABLE users (
    name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));
Run Code Online (Sandbox Code Playgroud)


Bax*_*Bax 6

锁定有区别。
添加索引不会阻止对表的读取访问。
添加约束确实会放置表锁(因此所有选择都被阻止),因为它是通过ALTER TABLE添加的。


Luk*_*der 6

仅使用约束而不使用索引可以完成的一件非常小的事情是使用ON CONFLICT ON CONSTRAINT子句(另请参阅此问题)。

这不起作用:

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 *;
Run Code Online (Sandbox Code Playgroud)

它产生:

[42704]: ERROR: constraint "u" for table "t" does not exist
Run Code Online (Sandbox Code Playgroud)

将索引变成约束:

[42704]: ERROR: constraint "u" for table "t" does not exist
Run Code Online (Sandbox Code Playgroud)

INSERT语句现在有效。

  • 但在唯一索引上,您仍然可以通过在大括号中列出字段来完成:ON CONFLICT (b) DO UPDATE SET (3认同)