简体   繁体   中英

How to find whether unique key constraint exists for given columns

I am working on a perl script, where i need to run update queries. But I need to check if the update sql command does not violate unique key constraint. So if I have a table tb(C1,C2,C3) and my update query is like:

update tb set C1='b1' where C2='a1' ;

Is there a way to find if a unique key constraint exist for column C1,C2 before trying to update? Ie: UNIQUE(C1,C2) .

You can query the system catalogs for unique constraints , in particular pg_constraint and 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;
  • The object identifer type regclass helps to unambiguously identify your table.

  • The system catalog information function pg_get_constraintdef() gets you nicely formatted information, which is not strictly necessary for your request.

  • Also using array operators <@ and @> to make sure the arrays match completely. (The order of columns is unknown.) The system columns are smallint and smallint[] respectively. Cast to integer to make it work with those operators.

  • Column names are case sensitive when looking them up in the system catalog directly. If you didn't double-quote C1 and C2 at creation time, you have to use c1 and c2 in this context.

  • There could also be a multicolumn primary key constraint enforcing uniqueness. To cover that in the query use instead:

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

Building on @Roman's fiddle, this one also demonstrates the pk case:

->SQLfiddle

Unique index

Both of the above (unique & pk constraints) are implemented by way of a unique index. In addition there can also be unique indices doing effectively the same as formally declared unique constraint. To catch all of them query the system catalog pg_index instead, in a similar fashion:

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;

Special difficulty here is the internal type int2vector . I deal with it by casting text and converting to int[] .

Be aware that implementation of catalog tables might change across major. Unlikely that these queries break, but possible.

You can find out whether a constraint exists (non-portably) by inspecting the pg_catalog.pg_constraint table, but that won't tell you whether the constraint would be violated by your insert, and it would be prone to races even if it could.

The correct thing to do is to try the insert and handle a failure appropriately.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM