I have to implement a rule that only one ‘group’ with a given ID can have status ‘In Progress’ or ‘Problem’ at one time. Is it possible to represent this in a Postgres check, or would I have to resort to logic in the application?
For example:
INSERT INTO group (group_id, status) VALUES (1, 'In Progress'); -- okay
INSERT INTO group (group_id, status) VALUES (2, 'Problem'); -- okay
INSERT INTO group (group_id, status) VALUES (3, 'In Progress'); -- okay
INSERT INTO group (group_id, status) VALUES (4, 'Problem'); -- okay
INSERT INTO group (group_id, status) VALUES (1, 'Something else'); -- okay
INSERT INTO group (group_id, status) VALUES (2, 'Foo bar'); -- okay
INSERT INTO group (group_id, status) VALUES (1, 'In Progress'); -- should fail
INSERT INTO group (group_id, status) VALUES (1, 'Problem'); -- should fail
INSERT INTO group (group_id, status) VALUES (2, 'In Progress'); -- should fail
INSERT INTO group (group_id, status) VALUES (2, 'Problem'); -- should fail
I think you need a partial unique index.
However, it isn’t clear to me from your description why the second expected-failure would fail. Do you want to allow only one of
In ProgressorProblemfor any givengroup_id? If so, you could write something like:… omitting the
statusfrom the partial unique index and using it only for the predicate. See this SQLFiddle.Note that this cannot be expressed as a
UNIQUEconstraint, since unique constraints do not take a predicate. A PostgreSQL unique constraint is implemented using a (non-partial)UNIQUEindex, but it also creates metadata entries that the mere index creation does not. A partial unique index works like a unique constraint with a predicate, but it won’t be discoverable via metadata likeinformation_schema‘s constraint info.