I have a many-to-many join table in Postgres that I would like to index to A) increase performance (obviously) and B) enforce uniqueness. For example:
a_id | b_id
1 | 2 <- okay
1 | 3 <- okay
2 | 3 <- okay
1 | 3 <- not okay (same as row 2)
Is it possible to have a single index on two columns that enforces uniqueness in the values? What type of index should I use?
You can create an index on more than one column of a table. This index is called a multicolumn index, a composite index, a combined index, or a concatenated index. A multicolumn index can have maximum 32 columns of a table. The limit can be changed by modifying the pg_config_manual.
A multicolumn GiST index can be used with query conditions that involve any subset of the index's columns. Conditions on additional columns restrict the entries returned by the index, but the condition on the first column is the most important one for determining how much of the index needs to be scanned.
A unique index ensures the index key columns do not contain any duplicate values. A unique index may consist of one or many columns. If a unique index has one column, the values in this column will be unique. In case the unique index has multiple columns, the combination of values in these columns is unique.
Do this if that unique is primary key:
create table tbl(
a_id int not null,
b_id int not null,
constraint tbl_pkey primary key(a_id,b_id)
);
Do this if that unique is non-primary key:
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)
);
If you have existing table, do this instead:
alter table tbl
add constraint tbl_unique unique(a_id, b_id)
That alter table display this message:
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "tbl_unique" for table "tbl"
Query returned successfully with no result in 22 ms.
If you wanted to drop that constraint(you might want to make unique a combination of 3 fields):
ALTER TABLE tbl DROP CONSTRAINT tbl_unique;
Regarding index, from Postgres doc:
PostgreSQL automatically creates a unique index when a unique constraint or primary key is defined for a table
Source: http://www.postgresql.org/docs/9.1/static/indexes-unique.html
If uniqueness depends on some rules, you shall use CREATE UNIQUE INDEX
, for example:
Given this:
CREATE TABLE tbl
(
a_id integer NOT NULL,
b_id integer NULL
);
alter table tbl
add constraint tbl_unique unique(a_id, b_id);
That unique can catch these duplicates, this will be rejected by database:
insert into tbl values
(1,1),
(1,1);
Yet that UNIQUE CONSTRAINT cannot catch duplicate nulls. Nulls serves as unknown, they serves as wildcard, that's why it's allowed to have multiple nulls in unique constraint. This will be accepted by database:
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
Think of UNIQUE CONSTRAINT
that it allows deferred uniqueness, hence the acceptance of null values above.
If you want only one wildcard(null b_id) per a_id, aside from the unique constraint, you need to add a UNIQUE INDEX
. UNIQUE CONSTRAINT can't have an expression on them. INDEX
and UNIQUE INDEX
can. This will be your complete DDL for rejecting multiple null;
This will be your complete 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;
This will be rejected by your database now:
insert into tbl values
(1,1),
(1,null),
(1,null);
This will be allowed:
insert into tbl values
(1,1),
(1,null);
Related to http://www.ienablemuch.com/2010/12/postgresql-said-sql-server2008-said-non.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With