简体   繁体   English

PostgreSQL部分唯一索引和upsert

[英]PostgreSQL partial unique index and upsert

I'm trying to do an upsert to a table that has partial unique indexes 我正在尝试对具有部分唯一索引的表进行upsert

create table test (
    p text not null,
    q text,
    r text,
    txt text,
    unique(p,q,r)
);

create unique index test_p_idx on test(p) where q is null and r is null;
create unique index test_pq_idx on test(p, q) where r IS NULL;
create unique index test_pr_idx on test(p, r) where q is NULL;

In plain terms, p is not null and only one of q or r can be null. 简单来说, p不是null,只有qr可以为null。

Duplicate inserts throw constraint violations as expected 重复插入会按预期抛出约束违规

insert into test(p,q,r,txt) values ('p',null,null,'a'); -- violates test_p_idx
insert into test(p,q,r,txt) values ('p','q',null,'b'); -- violates test_pq_idx
insert into test(p,q,r,txt) values ('p',null, 'r','c'); -- violates test_pr_idx

However, when I'm trying to use the unique constraint for an upsert 但是,当我尝试使用唯一约束进行upsert时

insert into test as u (p,q,r,txt) values ('p',null,'r','d') 
on conflict (p, q, r) do update 
set txt = excluded.txt

it still throws the constraint violation 它仍然会引发约束违规

ERROR: duplicate key value violates unique constraint "test_pr_idx" DETAIL: Key (p, r)=(p, r) already exists. 错误:重复键值违反唯一约束“test_pr_idx”DETAIL:键(p,r)=(p,r)已存在。

But I'd expect the on conflict clause to catch it and do the update. 但我希望on conflict子句可以捕获它并进行更新。

What am I doing wrong? 我究竟做错了什么? Should I be using an index_predicate ? 我应该使用index_predicate吗?

index_predicate Used to allow inference of partial unique indexes. index_predicate用于允许推断部分唯一索引。 Any indexes that satisfy the predicate (which need not actually be partial indexes) can be inferred. 可以推断出满足谓词的任何索引(实际上不需要是部分索引)。 Follows CREATE INDEX format. 遵循CREATE INDEX格式。 https://www.postgresql.org/docs/9.5/static/sql-insert.html https://www.postgresql.org/docs/9.5/static/sql-insert.html

I don't think it's possible to use multiple partial indexes as a conflict target. 我认为不可能将多个部分索引用作冲突目标。 You should try to achieve the desired behaviour using a single index. 您应该尝试使用单个索引实现所需的行为。 The only way I can see is to use a unique index on expressions: 我能看到的唯一方法是在表达式上使用唯一索引:

drop table if exists test;
create table test (
    p text not null,
    q text,
    r text,
    txt text
);

create unique index test_unique_idx on test (p, coalesce(q, ''), coalesce(r, ''));

Now all three tests (executed twice) violate the same index: 现在所有三个测试(执行两次)都违反了相同的索引:

insert into test(p,q,r,txt) values ('p',null,null,'a'); -- violates test_unique_idx
insert into test(p,q,r,txt) values ('p','q',null,'b');  -- violates test_unique_idx
insert into test(p,q,r,txt) values ('p',null, 'r','c'); -- violates test_unique_idx

In the insert command you should pass the expressions used in the index definition: 在insert命令中,您应该传递索引定义中使用的表达式:

insert into test as u (p,q,r,txt) 
values ('p',null,'r','d') 
on conflict (p, coalesce(q, ''), coalesce(r, '')) do update 
set txt = excluded.txt;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM