Вам нужна комбинация частичного уникального индекса и исключающего ограничения .К сожалению, нет семейства операторов, которое можно было бы использовать для логического столбца в ограничении исключения, поэтому вместо него можно использовать целочисленный столбец.Расширение btree_gist необходимо для эмуляции индекса gist для целочисленных столбцов.
create extension if not exists btree_gist;
Определение таблицы (идентификаторы немного изменены):
drop table if exists my_table;
create table my_table (
user_id integer,
type_id integer,
flag integer check (flag in (0, 1)),
text varchar (50),
exclude using gist (user_id with =, type_id with =, flag with <>)
);
create unique index on my_table (user_id, type_id) where flag = 1;
Примерные вставки:
insert into my_table
values
(1, 1, 0, 'foo'),
(1, 1, 0, 'bar'),
(2, 2, 1, 'foo');
INSERT 0 3
insert into my_table
values
(1, 1, 1, 'whatever');
ERROR: conflicting key value violates exclusion constraint "my_table_user_id_type_id_flag_excl"
DETAIL: Key (user_id, type_id, flag)=(1, 1, 1) conflicts with existing key (user_id, type_id, flag)=(1, 1, 0).
insert into my_table
values
(2, 2, 0, 'whatever');
ERROR: conflicting key value violates exclusion constraint "my_table_user_id_type_id_flag_excl"
DETAIL: Key (user_id, type_id, flag)=(2, 2, 0) conflicts with existing key (user_id, type_id, flag)=(2, 2, 1).
insert into my_table
values
(2, 2, 1, 'whatever');
ERROR: duplicate key value violates unique constraint "my_table_user_id_type_id_idx"
DETAIL: Key (user_id, type_id)=(2, 2) already exists.