2017-10-13 99 views
1

我試圖做一個更新插入到具有表部分唯一索引PostgreSQL的部分唯一索引並更新插入

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不爲空,而只有一個qr可以爲null 。

重複插入扔約束違規預期

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 

它仍然拋出約束違反

但我希望on conflict子句t抓住它並做更新。

我在做什麼錯?我應該使用index_predicate嗎?

index_predicate Used to allow inference of partial unique indexes. Any indexes that satisfy the predicate (which need not actually be partial indexes) can be inferred. Follows CREATE INDEX format. https://www.postgresql.org/docs/9.5/static/sql-insert.html

+0

https://stackoverflow.com/a/46728249/330315 –

+0

的[部分索引可能的複製,同時執行了在Postgres一個UPSERT不使用關於衝突子句QL(https://stackoverflow.com/questions/46727740/partial-index-not-used-in-on-conflict-clause-while-performing-an-upsert-in-postg) – Eelke

回答

2

我不認爲這是可能使用多個部分索引作爲衝突的目標。您應該嘗試使用單個索引實現所需的行爲。我能看到的唯一方式是使用在表達式唯一索引:

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 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 

在插入命令,你應該通過在索引定義中使用的表達式:

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;