PostgreSQL部分索引和UPSERT

cin*_*ata 7 sql postgresql unique upsert

经过谷歌搜索后,我的问题描述如下:

CREATE TABLE security (
  id          SERIAL PRIMARY KEY,
  vendor      VARCHAR(20),
  external_id VARCHAR(20),
  extinct     BOOLEAN DEFAULT FALSE
);

CREATE UNIQUE INDEX unique_vendor ON security(vendor, extinct) where vendor is not null;
CREATE UNIQUE INDEX unique_external_id ON security(external_id, extinct) where external_id is not null;
Run Code Online (Sandbox Code Playgroud)

试图插入值:

insert into security (vendor, external_id, extinct) 
  values('Legion', 'LGNONE', false)
  ON CONFLICT(vendor, external_id, extinct) DO UPDATE
  SET vendor = 'Legion', external_id = 'LGNONE', extinct = false;
Run Code Online (Sandbox Code Playgroud)

结果是:

[42P10] ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
Run Code Online (Sandbox Code Playgroud)

Altho这个工作(按规格):

insert into security (vendor, external_id, extinct) 
    values('Legion', 'LGNONE', false)
    ON CONFLICT DO NOTHING;
Run Code Online (Sandbox Code Playgroud)

PostgreSQL文档表明它应该工作

PostgreSQL v9.5

我的目标是找到在多个可空列上在此表上创建唯一索引的方法,并在UPSERT上使用新行更新旧行

kli*_*lin 12

conflict_target使用中on conflict必须标识现有唯一索引.你不能使用

on conflict (vendor, external_id, extinct)
Run Code Online (Sandbox Code Playgroud)

因为三列上没有索引.Postgres结合多个索引以满足您的冲突目标并不是那么聪明.

但是,您可以像这样创建一个部分索引:

create unique index unique_vendor_external_id 
    on security(vendor, external_id, extinct) 
    where coalesce(vendor, external_id) is not null;
Run Code Online (Sandbox Code Playgroud)

现在您可以将这三列用作冲突目标:

insert into security (vendor, external_id, extinct) 
    values('Legion', 'LGNONE', false)
on conflict (vendor, external_id, extinct)          -- exact match to the index definition 
    where coalesce(vendor, external_id) is not null -- obligatory index_predicate
do update set
    vendor = excluded.vendor,
    external_id = excluded.external_id,
    extinct = excluded.extinct
Run Code Online (Sandbox Code Playgroud)

注意使用特殊记录excluded.对于文档:

ON CONFLICT DO UPDATE中的SET和WHERE子句可以使用表的名称(或别名)访问现有行,并使用特殊排除表访问建议插入的行.