我试图对具有部分唯一索引的表进行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;
简单来说,p
不为空,只有q
或r
中的一个可以为空。
重复插入会按预期抛出约束违规
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
然而,当我尝试使用唯一约束进行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
它仍会抛出约束违规
错误:重复键值违反了唯一约束" test_pr_idx" 细节:键(p,r)=(p,r)已经存在。
但是我希望on conflict
子句能够捕获它并进行更新。
我做错了什么?我应该使用index_predicate
吗?
index_predicate 用于允许推断部分唯一索引。任何 满足谓词的索引(实际上不必是部分的 索引)可以推断出来。遵循CREATE INDEX格式。 https://www.postgresql.org/docs/9.5/static/sql-insert.html
答案 0 :(得分:3)
我认为不可能将多个部分索引用作冲突目标。您应该尝试使用单个索引实现所需的行为。我能看到的唯一方法是在表达式上使用唯一索引:
$posts = Post::withCount('upvotes')
->having('upvotes_count', '>', 5)
->get();
现在所有三个测试(执行两次)都违反了相同的索引:
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, ''));
在insert命令中,您应该传递索引定义中使用的表达式:
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