2015-06-11 25 views
5

Ho tabella di PostgreSQLPostgreSQL valore unico acreoss più colonne

id ColA ColB 
------------------ 
1  'a'  'b' 
2  'c'  'd' 

voglio fare valori in Cola e ColB essere univoco in entrambe le colonne, cioè qualsiasi di questi inserti sarebbe vietato:

INSERT INTO table (ColA,ColB) values('a','e'); 
INSERT INTO table (ColA,ColB) values('z','a'); 
INSERT INTO table (ColA,ColB) values('d','g'); 

e uno di questi inserti sarebbe permesso:

INSERT INTO table (ColA,ColB) values('z','e'); 
INSERT INTO table (ColA,ColB) values('l','k'); 

Così

CONSTRAINT unique_name UNIQUE (ColA,ColB) 

non è adatto, perché consentirà uno dei 4 precedenti inserti.

risposta

2

Purtroppo, questo non può essere risolto facilmente con semplici contrappunti/indici unici (se può essere risolto con loro affatto).

Quello che vi serve, è un exclusionconstraint: la possibilità di escludere alcune righe, sulla base di qualcosa come collisione. I vincoli unici sono solo specifici vincoli di esclusione (sono basati sull'uguaglianza collisioni).

Quindi, in teoria, è sufficiente per escludere ogni row1, dove c'è già un row2, per il quale questa espressione è vero: ARRAY[row1.cola, row1.colb] && ARRAY[row2.cola, row2.colb]

Questo indice potrebbe fare il lavoro (supporto attualmente solo gist indici vincoli di esclusione):

ALTER TABLE table_name 
    ADD CONSTRAINT table_name_exclusion 
    EXCLUDE USING gist ((ARRAY[cola, colb]) WITH &&); 

Ma purtroppo, non esiste una categoria operatore predefinito per le matrici (che utilizza gist). C'è uno intarray module, che fornisce uno solo per gli array integer, ma nulla per gli array text.

Se davvero si vuole lavorare su questo, si può sempre abusare del range types (p.es. ho usato il adiacente -|- dell'operatore, che gestisce tutti i casi, che non possono essere trattate con unique) ...

-- there is no built-in type for text ranges neither, 
-- but it can can be created fairly easily: 
CREATE TYPE textrange AS RANGE (
    SUBTYPE = text 
); 

ALTER TABLE table_name 
    ADD CONSTRAINT table_name_exclusion 
    EXCLUDE USING gist ((textrange(least(cola, colb), greatest(cola, colb))) WITH -|-); 

-- the exclusion constraint above does not handle all situations: 

ALTER TABLE table_name 
    ADD CONSTRAINT table_name_check 
    CHECK (cola is distinct from colb); -- without this, empty ranges could be created, 
             -- which are not adjacent to any other range 

CREATE UNIQUE INDEX table_name_unique 
    ON table_name ((ARRAY[least(cola, colb), greatest(cola, colb)])); 
    -- without this, duplicated rows could be created, 
    -- because ranges are not adjacent to themselves 

... ma temo, il tuo problema originale potrebbe essere risolto molto più facilmente con un piccolo refactoring del database; che ci porta alla domanda: quale problema, vuoi risolvere con questo?