How to find whether unique key constraint exists for given columns

故事扮演 提交于 2019-12-01 21:54:53

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!