2014-05-08 70 views
88

,我可以理解documentation以下定義是等價的:Postgres的唯一約束VS指數

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。 ..添加約束。使用索引來執行唯一約束可以被視爲不應該直接訪問的實現細節

這只是一個很好的風格問題嗎?選擇這些變體中的一個(例如,在演奏中)會有什麼實際後果?

+19

的(唯一的)實際區別是,你可以創建一個外鍵,唯一約束,但不是唯一的指標。 –

+20

其他方式的一個優點([最近在另一個問題中提出]](http://stackoverflow.com/a/23449309/157957))是你可以有一個* partial *唯一索引,比如「Unique( foo)哪裏酒吧是空的「。 AFAIK,沒有辦法做到這一點的約束。 – IMSoP

+0

[Here](http://flatiron.engineering/technology/2016/09/13/uniqueness-in-postgres.html)關於差異的好文章 –

回答

82

我對這個基本而重要的問題有些懷疑,所以我決定以身作則。

讓我們來創建測試表有兩列,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=# 

它按預期工作!

外鍵

現在,我們將定義詳細表有兩個外鍵引用到我們的兩列

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 ...definition。 在唯一索引聲明中,您可以設置WHERE clause來創建部分索引。 你也可以用create index表達式(不僅在列上)定義一些其他參數(排序規則,排序規則,NULLs佈局)。

您不能使用部分索引添加表約束。使用UNIQUE INDEXUNIQUE CONSTRAINT

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. 
21

還有一個好處是,你可以很容易地DROP/CREATE指數CONCURRENTLY,而與約束你不能。

+2

AFAIK不可能同時丟棄一個唯一的索引。 https://www.postgresql.org/docs/9.3/static/sql-dropindex.html「使用此選項時需注意幾個注意事項,只能指定一個索引名稱,並且不支持CASCADE選項(因此,支持UNIQUE或PRIMARY KEY約束的索引不能以這種方式丟棄。)「 –

2

唯一性是一個約束。它恰好通過創建一個唯一索引 來實現,因爲索引可以快速搜索所有現有的 值,以確定給定值是否已經存在。

概念上,索引是一個實現細節,唯一性應該是 僅與約束關聯。

The full text

所以速度表現應該是相同