簡體   English   中英

PostgreSQL部分唯一索引和upsert

[英]PostgreSQL partial unique index and upsert

我正在嘗試對具有部分唯一索引的表進行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不是null,只有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

它仍然會引發約束違規

錯誤:重復鍵值違反唯一約束“test_pr_idx”DETAIL:鍵(p,r)=(p,r)已存在。

但我希望on conflict子句可以捕獲它並進行更新。

我究竟做錯了什么? 我應該使用index_predicate嗎?

index_predicate用於允許推斷部分唯一索引。 可以推斷出滿足謂詞的任何索引(實際上不需要是部分索引)。 遵循CREATE INDEX格式。 https://www.postgresql.org/docs/9.5/static/sql-insert.html

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

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命令中,您應該傳遞索引定義中使用的表達式:

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