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)
.
SQL UNIQUE Constraint. The UNIQUE constraint ensures that all values in a column are different. Both the UNIQUE and PRIMARY KEY constraints provide a guarantee for uniqueness for a column or set of columns. A PRIMARY KEY constraint automatically has a UNIQUE constraint.
–Adding unique key constraint to a column. ADD CONSTRAINT <identifier_name> UNIQUE (<column_name1>, <column_name2>,…); ALTER TABLE demo ADD UNIQUE (PHONE); --Let's insert a row into the table.
However, you can have many UNIQUE constraints per table, but only one PRIMARY KEY constraint per table. The following SQL creates a UNIQUE constraint on the "ID" column when the "Persons" table is created:
set the owner, table and column of interest and it will show you all constraints that cover that column Note that this won't show all cases where a unique index exists on a column (as its possible to have a unique index in place without a constraint being present).
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With