snippetsqlModerate
Create a PostgreSQL constraint to prevent unique combination rows
Viewed 0 times
postgresqluniquepreventcombinationrowscreateconstraint
Problem
Imagine you have a simple table:
I need to create a special unique constraint which fails on following situation:
different
Example of permitted condition:
Note: simple multi-column unique index won't permit combination like this.
Example of permitted condition:
Example of failed condition:
Ideally, I need unique constraint or unique partial index. Triggers are more problematic for me.
Double
name | is_active
----------------
A | 0
A | 0
B | 0
C | 1
... | ...I need to create a special unique constraint which fails on following situation:
different
is_active values can't co-exist for the same name value.Example of permitted condition:
Note: simple multi-column unique index won't permit combination like this.
A | 0
A | 0
B | 0Example of permitted condition:
A | 0
B | 1Example of failed condition:
A | 0
A | 1
-- should be prevented, because `A 0` exists
-- same name, but different `is_active`Ideally, I need unique constraint or unique partial index. Triggers are more problematic for me.
Double
A,0 allowed, but (A,0) (A,1) isn't.Solution
You can use an exclusion constraint with
Then we add a constraint that says:
"We can't have 2 rows that have the same
Some notes:
btree_gist,-- This is needed
CREATE EXTENSION btree_gist;Then we add a constraint that says:
"We can't have 2 rows that have the same
name and different is_active":ALTER TABLE table_name
ADD CONSTRAINT only_one_is_active_value_per_name
EXCLUDE USING gist
( name WITH =,
is_active WITH <> -- if boolean, use instead:
-- (is_active::int) WITH <>
);Some notes:
is_activecan be integer or boolean, makes no difference for the exclusion constraint. (actually it does, if the column is boolean you need to use(is_active::int) WITH <>.)
- Rows where
nameoris_activeis null will be ignored by the constraint and thus allowed.
- The constraint makes sense only if the table has more columns. Otherwise, if the table has only these 2 columns, a
UNIQUEconstraint on(name)alone would be easier and more appropriate. I don't see any reason for storing multiple identical rows.
- The design violates 2NF. While the exclusion constraint will save us from update anomalies, it may not from performance issues. If you have for example 1000 rows with
name = 'A'and you want to to update is_active status from 0 to 3, all 1000 will have to be updated. You should examine whether normalizing the design would be more efficient. (Normalizing meaning in this case to remove is_active status from the table and add a 2-column table with name, is_active and a unique constraint on(name). Ifis_activeis boolean, it could be totally stripped and the extra table just a single column table, storing only the "active" names.)
Code Snippets
-- This is needed
CREATE EXTENSION btree_gist;ALTER TABLE table_name
ADD CONSTRAINT only_one_is_active_value_per_name
EXCLUDE USING gist
( name WITH =,
is_active WITH <> -- if boolean, use instead:
-- (is_active::int) WITH <>
);Context
StackExchange Database Administrators Q#161506, answer score: 19
Revisions (0)
No revisions yet.