Memorizza come array (denormalizzato)
Prenderei in considerazione il modulo aggiuntivo intarray
che fornisce le comode (e veloci) funzioni uniq()
e sort()
. In una tipica installazione moderna di Postgres è facile come:
CREATE EXTENSION intarray;
Usando questi, un semplice CHECK
il vincolo può imporre il crescente array con distinti elementi.
CHECK (uniq(sort(cat_arr)) = cat_arr)
Puoi in aggiunta (facoltativamente) avere un trigger che normalizza i valori dell'array ON INSERT OR UPDATE
automaticamente. Quindi puoi semplicemente passare qualsiasi array (possibilmente non ordinato e con duplicati) e tutto funziona. Come:
CREATE OR REPLACE FUNCTION trg_search_insup_bef()
RETURNS trigger AS
$func$
BEGIN
NEW.cat_arr := uniq(sort(NEW.cat_arr);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF cat_arr ON search
FOR EACH ROW
EXECUTE PROCEDURE trg_search_insup_bef();
Il modulo aggiuntivo intarray è opzionale, ci sono altri modi:
Ma le funzioni intraray offrono prestazioni superiori.
Allora puoi semplicemente creare un UNIQUE
vincolo sulla colonna dell'array per imporre l'unicità dell'intero array.
UNIQUE (cat_arr)
Ho scritto di più sui vantaggi della combinazione di vincoli (molto severi e affidabili) con trigger (meno affidabili ma più convenienti) in questa risposta correlata solo due giorni fa:
Se, per ogni combinazione, tutto ciò che devi memorizzare per categoria è l'ID (e nessuna informazione aggiuntiva), questo dovrebbe essere abbastanza buono.
Tuttavia , l'integrità referenziale non è facilmente assicurata in questo modo. Non ci sono (ancora) vincoli di chiave esterna per gli elementi dell'array, come documentato nel tuo link
:Se una delle categorie viene eliminata o si modificano gli ID, i riferimenti si interrompono...
Schema normalizzato
Se hai bisogno di archiviare di più o preferisci utilizzare uno schema normalizzato per imporre l'integrità referenziale o per qualche motivo, puoi farlo anche tu e aggiungere un trigger per popolare una vista materializzata fatta a mano (una tabella ridondante) e imporre l'unicità in modo simile:
CREATE TABLE search (
search_id serial PRIMARY KEY
, ... more columns
);
CREATE TABLE cat (
cat_id serial PRIMARY KEY
, cat text NOT NULL
);
CREATE TABLE search_cat (
search_id int REFERENCES search ON DELETE CASCADE
, cat_id int REFERENCES cat
, PRIMARY KEY (search_id, cat_id)
);
Risposta correlata (non per combinazioni uniche, ma per elementi unici) che dimostra il trigger: