2012-05-06 24 views
4

我有一个在Postgres中的多对多连接表,我想索引到A)增加性能(显然)和B)强制唯一性。例如:Postgres独特的多列索引连接表

a_id | b_id 
1 | 2  <- okay 
1 | 3  <- okay 
2 | 3  <- okay 
1 | 3  <- not okay (same as row 2) 

是否有可能在两列上实现一个强制唯一性的值的索引?我应该使用什么类型的索引?

回答

11

作为主键

这样做,如果是唯一的主键:

create table tbl(
    a_id int not null, 
    b_id int not null, 
    constraint tbl_pkey primary key(a_id,b_id) 
); 

不是主键

这样做,如果是唯一的非主键:

create table tbl(

    -- other primary key here, e.g.: 
    -- id serial primary key, 

    a_id int not null, 
    b_id int not null, 
    constraint tbl_unique unique(a_id,b_id) 
); 

现有的表

如果现有的表,而是执行此操作:

alter table tbl 
     add constraint tbl_unique unique(a_id, b_id) 

能改变表显示此消息:

NOTICE: ALTER TABLE/ADD UNIQUE will create implicit index "tbl_unique" for table "tbl" 


Query returned successfully with no result in 22 ms. 

如果你想丢弃约束(您可能要使3个字段的组合独特):

ALTER TABLE tbl DROP CONSTRAINT tbl_unique; 

索引&约束&空值

关于索引,从Postgres的DOC:

PostgreSQL自动当一个独特 约束或主键对表

源定义的唯一索引: http://www.postgresql.org/docs/9.1/static/indexes-unique.html


如果唯一依赖于一些规则,您应使用CREATE UNIQUE INDEX,例如:

鉴于此:

CREATE TABLE tbl 
(
    a_id integer NOT NULL, 
    b_id integer NULL 
); 

alter table tbl 
    add constraint tbl_unique unique(a_id, b_id); 

独特能赶上这些重复,这将是由数据库拒绝:

insert into tbl values 
(1,1), 
(1,1); 

然而,唯一约束不能捕获重复的空值。空值用作未知数,它们用作通配符,这就是为什么允许有多个空值的唯一约束。这将通过数据库被接受:中UNIQUE CONSTRAINT

insert into tbl values 
(1,1), 
(1,null), -- think of this null as wildcard, some real value can be assigned later. 
(1,null); -- and so is this. that's why both of these nulls are allowed 

认为它允许延期的独特性,因此接受上述空值。

如果每个a_id只需一个通配符(空b_id),除了唯一约束之外,您需要添加一个UNIQUE INDEX。唯一约束不能有一个表达式。 INDEXUNIQUE INDEX即可。这将是您完整的DDL拒绝多个null;

这将是你的完整DDL:

CREATE TABLE tbl 
(
    a_id integer NOT NULL, 
    b_id integer NULL 
); 
alter table tbl 
    add constraint tbl_unique unique(a_id, b_id); 

create unique index tbl_unique_a_id on tbl(a_id) where b_id is null;  

这将通过你的数据库现在被拒绝:

insert into tbl values 
(1,1), 
(1,null), 
(1,null); 

这将被允许:

insert into tbl values 
(1,1), 
(1,null); 

相关http://www.ienablemuch.com/2010/12/postgresql-said-sql-server2008-said-non.html

+2

请注意,虽然(a_id,b_id)上的多列索引也可用于仅在a_id上进行搜索,但它不能用于仅b_id上的搜索。所以你可能想在b_id上创建第二个单列索引。 – Eelke

+0

@Eelke感谢您添加该信息。我链接到这里的文档以供参考:http://www.postgresql.org/docs/7.4/static/indexes-multicolumn.html我只是想知道他们为什么不再强调多列索引仍然可以被使用版本9.1文档中单独使用a_id(但不能使用b_id)doc http://www.postgresql.org/docs/9.1/static/indexes-multicolumn.html –

+0

@Eelke很棒的观察!当我解释一些查询并且切换索引的顺序来修复它时,我只是有一个WTF时刻。 – bloudermilk

5

除了使用由@迈克尔布恩作为解释PRIMARY KEYUNIQUE语法,你也可以创建一个明确的指标:

CREATE UNIQUE INDEX foo_a_b ON tbl(a_id, b_id); 

这仅仅是一个正常的,多列B树索引(这正是KEY语法隐式创建)。