UPSERT - INSERT ... ON CONFLICT 失败,基于函数的索引作为 'lower()' 唯一约束

UPSERT - INSERT ... ON CONFLICT fails with a function based index as 'lower()' unique constraint

我有一个数据库 table,它有一个 code 列,该列使用小写索引来防止代码值仅大小写不同(例如 'XYZ' = 'xYZ' = 'xyz')。 Postgresql 中的典型方法是创建一个基于函数的索引,如下所示:CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code)).

现在我遇到一个情况,我需要对该列进行更新插入行为:

-- first insert
INSERT INTO mytable (code) VALUES ('abcd');

-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
  ON CONFLICT (code) DO UPDATE
  SET code='Abcd';

对于第二次插入,我得到了唯一键违规:ERROR: duplicate key value violates unique constraint "mytable_lower_code_idx"

(我也尝试过使用 ON CONFLICT ON CONSTRAINT mytable_lower_code_idx 但 Postgresql 告诉我这个约束不存在,所以它可能不会将索引视为约束。)

我的最后一个问题:有什么方法可以使 INSERT ... ON CONFLICT 与表达式的索引一起工作?还是我必须引入物理索引小写列才能完成任务?

尝试按如下方式添加索引:

CREATE UNIQUE INDEX CONCURRENTLY mytable_lower_code_idx 
ON mytable (lower(code));

ALTER TABLE mytable 
ADD CONSTRAINT unique_mytab_code
UNIQUE USING INDEX mytable_lower_code_idx ;

然后:

INSERT INTO mytable (code) VALUES ('abcd');

-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
  ON CONFLICT ON CONSTRAINT unique_mytab_code DO UPDATE
  SET code='Abcd';

使用ON CONFLICT (lower(code)) DO UPDATE:

CREATE TABLE mytable (
    code text
);
CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code));
INSERT INTO mytable VALUES ('abcd');

INSERT INTO mytable (code) VALUES ('Abcd')
  ON CONFLICT (lower(code)) DO UPDATE
  SET code='Abcd';

SELECT * FROM mytable;

产量

| code |
|------|
| Abcd |

请注意 ON CONFLICT syntax 允许冲突目标是 index_expression(我强调的):

ON CONFLICT conflict_target
where conflict_target can be one of:

( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
    ON CONSTRAINT constraint_name

and index_expression:

Similar to index_column_name, but used to infer expressions on table_name columns appearing within index definitions (not simple columns). Follows CREATE INDEX format.