PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Restrição exclusiva do Postgres vs índice


Eu tinha algumas dúvidas sobre essa questão básica, mas importante, então decidi aprender pelo exemplo.

Vamos criar a tabela de teste master com duas colunas, con_id com restrição exclusiva e ind_id indexado por índice único.
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)

Na descrição da tabela (\d no psql) você pode diferenciar a restrição exclusiva do índice exclusivo.

Singularidade

Vamos verificar a exclusividade, apenas no caso.
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=#

Funciona como esperado!

Chaves estrangeiras

Agora vamos definir detalhe tabela com duas chaves estrangeiras referenciando nossas duas colunas em 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)

Bem, sem erros. Vamos garantir que funcione.
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=#

Ambas as colunas podem ser referenciadas em chaves estrangeiras.

Restrição de uso de índice

Você pode adicionar restrição de tabela usando o índice exclusivo existente.
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)

Agora não há diferença entre a descrição das restrições de coluna.

Índices parciais

Na declaração de restrição de tabela você não pode criar índices parciais. Isso vem diretamente da definição de create table ... .Na declaração de índice único você pode definir a WHERE clause para criar índice parcial. Você também pode criar índice na expressão (não apenas na coluna) e definir alguns outros parâmetros (agrupamento, ordem de classificação, colocação de NULLs).

Você não pode adicionar restrição de tabela usando índice parcial.
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.