Avevo dei dubbi su questa questione fondamentale ma importante, quindi ho deciso di imparare con l'esempio.
Creiamo la tabella di test master con due colonne, con_id con vincolo univoco e ind_id indicizzato per indice univoco.
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)
Nella descrizione della tabella (\d in psql) puoi distinguere il vincolo univoco dall'indice univoco.
Unicità
Controlliamo l'unicità, per ogni evenienza.
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=#
Funziona come previsto!
Chiavi estere
Ora definiremo dettaglio tabella con due chiavi esterne che fanno riferimento alle nostre due colonne in 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)
Bene, nessun errore. Assicuriamoci che funzioni.
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=#
Entrambe le colonne possono essere referenziate in chiavi esterne.
Vincolo utilizzando l'indice
È possibile aggiungere un vincolo di tabella utilizzando l'indice univoco esistente.
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)
Ora non c'è differenza tra la descrizione dei vincoli di colonna.
Indici parziali
Nella dichiarazione di vincolo di tabella non puoi creare indici parziali. Deriva direttamente dalla definizione di create table ...
.Nella dichiarazione di indice univoco puoi impostare la WHERE clause
per creare un indice parziale. Puoi anche creare un indice sull'espressione (non solo sulla colonna) e definire alcuni altri parametri (confronto, ordinamento, posizionamento dei NULL).
Non puoi aggiungi il vincolo della tabella usando l'indice parziale.
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.