1. INSERT ... ON CONFLICT DO NOTHING / DO UPDATE 的 UPSERT 语义与冲突列(INFERRED INDEX)的指定方式?
解释 PostgreSQL 中 INSERT ... ON CONFLICT 的 UPSERT 语义,以及冲突列如何通过 INFERRED INDEX 指定?
- ON CONFLICT 的冲突检测机制
- DO NOTHING 与 DO UPDATE 的行为差异
- 冲突目标的指定方式(列名/索引表达式/约束)
ON CONFLICT 是 PostgreSQL 9.5+ 提供的 UPSERT(Upsert 即 Update + Insert)语法。当插入的行与唯一索引/唯一约束冲突时,可执行 DO NOTHING(跳过该行)或 DO UPDATE SET ...(更新已有行)。冲突目标通过 ON CONFLICT (col) 或 ON CONFLICT ON CONSTRAINT name 指定,必须能被数据库解析为一个唯一索引(即 INFERRED INDEX)。若省略冲突目标,则任何唯一约束冲突都会触发,但 DO UPDATE 时无法引用冲突行,因为无法推断具体是哪个索引。
冲突推断要求列必须匹配某个唯一索引(含索引表达式、部分索引需带 WHERE)。省略冲突目标时数据库扫描所有唯一索引,但若存在多个匹配则无法确定,需明确指定。
-- 指定冲突列
INSERT INTO users (id, name) VALUES (1, 'a')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
-- 指定约束名
INSERT INTO users (id, name) VALUES (1, 'a')
ON CONFLICT ON CONSTRAINT users_pkey DO NOTHING;
-- 省略冲突目标(不允许 DO UPDATE 引用该行)
INSERT INTO users (id, name) VALUES (1, 'a') ON CONFLICT DO NOTHING;