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

Come scoprire se esiste un vincolo di chiave univoca per determinate colonne

Puoi interrogare i cataloghi di sistema per vincoli unici , in particolare pg_constraint e pg_attribute :

SELECT c.conname, pg_get_constraintdef(c.oid)
FROM   pg_constraint c
JOIN  (
   SELECT array_agg(attnum::int) AS attkey
   FROM   pg_attribute
   WHERE  attrelid = 'tb'::regclass  -- table name optionally schema-qualified
   AND    attname  = ANY('{c1,c2}') 
   ) a ON c.conkey::int[] <@ a.attkey AND c.conkey::int[] @> a.attkey
WHERE  c.contype  = 'u'
AND    c.conrelid = 'tb'::regclass;
  • Il tipo di identificatore di oggetto regclass aiuta a identificare inequivocabilmente la tua tabella.

  • La funzione di informazioni del catalogo di sistema pg_get_constraintdef() ti fornisce informazioni ben formattate, che non sono strettamente necessarie per la tua richiesta.

  • Usando anche operatori di array <@ e @> per assicurarsi che gli array corrispondano completamente. (L'ordine delle colonne è sconosciuto.) Le colonne di sistema sono smallint e smallint[] rispettivamente. Trasmetti a integer per farlo funzionare con quegli operatori.

  • I nomi delle colonne fanno distinzione tra maiuscole e minuscole quando vengono cercati direttamente nel catalogo di sistema. Se non hai tra virgolette C1 e C2 al momento della creazione, devi usare c1 e c2 in questo contesto.

  • Potrebbe esserci anche un vincolo chiave primaria multicolonna imporre l'unicità. Per coprirlo nella query usa invece:

    WHERE  c.contype IN ('u', 'p')
    

Basandosi sul violino di @Roman, questo dimostra anche il caso pk:

->SQLfiddle

Indice unico

Entrambi i precedenti (vincoli univoci e pk) sono implementati tramite un indice univoco. Inoltre possono esserci anche indici unici facendo effettivamente la stessa cosa del vincolo unico formalmente dichiarato. Per catturarli tutti interrogare il catalogo di sistema pg_index invece, in modo simile:

SELECT c.relname AS idx_name
FROM  (
   SELECT indexrelid, string_to_array(indkey::text, ' ')::int[] AS indkey
   FROM   pg_index
   WHERE  indrelid = 'tb'::regclass
   AND    indisunique                    -- contains "indisprimary"
   ) i
JOIN  (
   SELECT array_agg(attnum::int) AS attkey
   FROM   pg_attribute
   WHERE  attrelid = 'tb'::regclass
   AND    attname  = ANY('{c1,c2}')
   ) a ON i.indkey <@ a.attkey AND i.indkey @> a.attkey
JOIN   pg_class c ON c.oid = i.indexrelid;

Difficoltà speciale qui è il tipo interno int2vector . Mi occupo di trasmettere il testo e di convertirlo in int[] .

Tieni presente che l'implementazione delle tabelle del catalogo potrebbe cambiare tra le principali. È improbabile che queste query si interrompano, ma possibile.