postgres 中相对于其他行的部分唯一索引

kam*_*lkp 3 postgresql indexing unique

我一般都知道 PostgreSQL 中的部分唯一索引,但我需要添加一个不适合我认为的部分索引领域的约束。或者也许有一种方法可以以某种方式表达它。

最小的例子

CREATE TABLE table (user INT, type INT, flag BOOL, text VARCHAR (50));
Run Code Online (Sandbox Code Playgroud)

要求是:

  1. Auser可以有多行相同的内容type,但前提是为flagfalse。

  2. 如果 a 的user一行包含特定的type和 aflag设置为 true,则该user和不能有其他行type

例如,如果表中有以下行:

| user | type | flag  | text |
| 1    | 1    | false | foo  |
| 1    | 1    | false | bar  |
Run Code Online (Sandbox Code Playgroud)

那么我们不能插入 (1, 1, true, 'whatever')


另外,如果表中有:

| user | type | flag | text |
| 1    | 1    | true | foo  |
Run Code Online (Sandbox Code Playgroud)

我们不能插入 (1, 1, false, 'bar') 也不能插入 (1, 1, true, 'baz')


有没有办法在 PostgreSQL 中表达这样的约束?

kli*_*lin 5

您需要部分唯一索引排除约束的组合。不幸的是,没有可用于排除约束中布尔列的运算符系列,因此可以使用整数列。btree_gist扩展对于模拟整数列的要点索引是必需的。

create extension if not exists btree_gist;
Run Code Online (Sandbox Code Playgroud)

表定义(标识符稍作修改):

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;
Run Code Online (Sandbox Code Playgroud)

示例插入:

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.
Run Code Online (Sandbox Code Playgroud)