PostgreSQL部分索引和UPSERT

时间:2017-02-12 11:44:40

标签: 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;

尝试插入值:

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;

结果:

[42P10] ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification

这可行(按规格):

insert into security (vendor, external_id, extinct) 
    values('Legion', 'LGNONE', false)
    ON CONFLICT DO NOTHING;

PostgreSQL documentation stands that it should work

PostgreSQL v9.5

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

1 个答案:

答案 0 :(得分:10)

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

on conflict (vendor, external_id, extinct)

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

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

create unique index unique_vendor_external_id 
    on security(vendor, external_id, extinct) 
    where coalesce(vendor, external_id) is not null;

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

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

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

  

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