全部术语

Upsert 插入或更新(INSERT ... ON CONFLICT)

PostgreSQL 中如何用 INSERT ... ON CONFLICT 实现 upsert?

INSERT ... ON CONFLICT 会插入一行;如果插入会违反唯一约束,则改为更新已有的行或跳过该行,整个过程是一个原子步骤。

Upsert 指插入或更新。PostgreSQL 通过 INSERT ... ON CONFLICT 实现它:当新行会违反唯一约束或排他约束时,DO NOTHING 会跳过该行,DO UPDATE 则改为更新发生冲突的行。

即使存在并发会话,ON CONFLICT DO UPDATE 也能保证插入或更新的结果是原子的,因此不需要先单独执行 SELECT,再执行 INSERT 或 UPDATE。特殊的 EXCLUDED 表指代被提议插入的那一行。冲突目标(例如 (path))必须与某个唯一索引或约束相匹配。

示例

该示例将同一条 upsert 语句运行两次。第一次插入 /home,浏览量为 1;第二次命中主键并加 1,因此最终计数为 2。

CREATE TABLE page_views (path text PRIMARY KEY, views bigint NOT NULL);

INSERT INTO page_views VALUES ('/home', 1)
ON CONFLICT (path) DO UPDATE SET views = page_views.views + EXCLUDED.views;
INSERT INTO page_views VALUES ('/home', 1)
ON CONFLICT (path) DO UPDATE SET views = page_views.views + EXCLUDED.views;

SELECT * FROM page_views;

适用于原生 PostgreSQL 16 或 17 的纯 SQL,无需任何扩展。请在一个可随时丢弃的空数据库中运行。

参考资料

PostgreSQL 官方文档(英文)。

相关术语