据我所知documentation,以下定义是等效的:
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);
但是您可以在注释中阅读:向表中添加唯一约束的首选方法是ALTER TABLE ... ADD CONSTRAINT。使用索引来强制执行唯一约束可以被视为不应直接访问的实现细节。
这只是一个好风格的问题吗?选择其中一种变体(例如性能)的实际后果是什么?
答案 0 :(得分:109)
我对这个基本但重要的问题有些怀疑,所以我决定通过实例学习。
让我们创建包含两列的测试表 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)
在表格描述(ps中的\ 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=#
按预期工作!
外键
现在我们将定义 detail 表,其中两个外键引用 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)
现在列约束描述之间没有区别。
部分索引
在表约束声明中,您无法创建部分索引。
它直接来自create table ...
的{{3}}。
在唯一索引声明中,您可以设置WHERE clause
以创建部分索引。
您还可以definition表达式(不仅在列上)并定义一些其他参数(排序规则,排序顺序,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.
答案 1 :(得分:26)
使用UNIQUE INDEX
与UNIQUE CONSTRAINT
的另一个好处是,您可以轻松地DROP
/ CREATE
索引CONCURRENTLY
,而有了约束,您可以“T
答案 2 :(得分:8)
唯一性是一种约束。它恰好通过创建来实现 一个唯一索引,因为索引可以快速搜索所有现有索引 值以确定给定值是否已存在。
从概念上讲,索引是一个实现细节,应该是唯一性 仅与约束相关联。
所以速度表现应该相同
答案 3 :(得分:4)
由于各种人都提供了唯一索引优于唯一约束的优点,所以有一个缺点:可以推迟唯一约束(仅在事务结束时检查),不能唯一索引。
答案 4 :(得分:2)
我遇到的另一件事是你可以在唯一索引中使用sql表达式,但不能在约束中使用。
所以,这不起作用:
CREATE TABLE users (
name text,
UNIQUE (lower(name))
);
但是后续工作。
CREATE TABLE users (
name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));
答案 5 :(得分:2)
使用ON CONFLICT ON CONSTRAINT
子句(see also this question)只能用约束而不用索引来完成一个非常小的事情。
这不起作用:
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
语句现在可以正常工作。
答案 6 :(得分:0)
我在文档中阅读了此内容
添加table_constraint [无效]
此表单使用与
CREATE TABLE
相同的语法以及选项NOT VALID
向表中添加新约束,该选项当前仅适用于外键约束。如果约束标记为NOT VALID
,则会跳过以验证表中所有行均满足约束的可能冗长的初始检查。仍然会针对后续插入或更新强制执行约束(也就是说,除非引用表中有匹配的行,否则约束将失败)。但是数据库不会假定该约束对表中的所有行均有效,直到使用VALIDATE CONSTRAINT选项对其进行验证为止。
因此,我认为您通过添加约束来称其为“部分唯一性”。
关于如何确保唯一性:
添加唯一约束将自动在约束中列出的列或一组列上创建唯一的B树索引。不能将仅覆盖某些行的唯一性限制写为唯一性约束,但是可以通过创建唯一的部分索引来实施这种限制。
注意:向表添加唯一约束的首选方法是ALTER TABLE…ADD CONSTRAINT。使用索引强制实施唯一约束可以被认为是不应直接访问的实现细节。但是,应该知道,无需在唯一列上手动创建索引;这样做只会复制自动创建的索引。
因此,我们应该添加约束来创建索引,以确保唯一性。
我怎么看这个问题?
一个“约束”旨在从语法上确保该列应该唯一,它建立了一条法律,一条规则;而“索引”是语义,是关于“如何实现,如何实现唯一性,实现时唯一性意味着什么”。因此,Postgresql实现它的方式非常合乎逻辑:首先,您声明一列应该是唯一的,然后,Postgresql添加为您添加唯一索引的实现。
答案 7 :(得分:0)
锁定有区别。
添加索引不会阻止对该表的读取访问。
添加约束确实会锁定表锁(因此所有选择都会被阻止),因为它是通过 ALTER TABLE 添加的。
答案 8 :(得分:-1)
SELECT a.phone_number,count(*) FROM public.users a
Group BY phone_number Having count(*)>1;
SELECT a.phone_number,count(*) FROM public.retailers a
Group BY phone_number Having count(*)>1;
select a.phone_number from users a inner join users b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id;
select a.phone_number from retailers a inner join retailers b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id
DELETE FROM
users a
USING users b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
DELETE FROM
retailers a
USING retailers b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
CREATE UNIQUE INDEX CONCURRENTLY users_phone_number
ON users (phone_number);
验证:
insert into users(name,phone_number,created_at,updated_at) select name,phone_number,created_at,updated_at from users