PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Valore univoco PostgreSQL su più colonne

Purtroppo, questo non può essere risolto facilmente con semplici vincoli/indici univoci (se può essere risolto con essi).

Ciò di cui hai bisogno è un'esclusione vincolo :la possibilità di escludere alcune righe, in base a qualcosa come collisione . I vincoli univoci sono solo vincoli di esclusione specifici (si basano su collisioni di uguaglianza). ).

Quindi, in teoria, devi solo escludere ogni row1 , dove esiste già una row2 , per cui questa espressione è vera:ARRAY[row1.cola, row1.colb] && ARRAY[row2.cola, row2.colb]

Questo indice potrebbe fai il lavoro (attualmente solo gist gli indici supportano i vincoli di esclusione):

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

Ma sfortunatamente, non esiste una classe operatore predefinita per gli array (che utilizza gist ). Esiste un intarray modulo , che ne fornisce uno solo per integer array, ma niente per text matrici.

Se vuoi davvero risolverlo, puoi sempre abusare di range tipi (es. ho usato l'adiacente -|- operatore, che gestisce tutti i casi, che non possono essere gestiti 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 che il tuo problema originale potrebbe essere risolto molto più facilmente con un piccolo refactoring del database; il che ci porta alla domanda:quale problema vuoi risolvere con questo?