on conflict do nothing 具备幂等性,但需精确匹配唯一约束定义:推荐用 on constraint 名而非列名;确保约束真实存在、有效且覆盖null场景;结合 returning 判断是否插入成功。

SQL 中 ON CONFLICT DO NOTHING 本身具备幂等性,但前提是冲突目标(ON CONFLICT ON CONSTRAINT 或 ON CONFLICT (column))必须**精确匹配唯一性约束的定义**,否则可能因索引/约束不一致导致重复插入或意外忽略。
明确指定冲突目标:用 CONSTRAINT 名而非列名
推荐显式引用唯一约束名,避免因列顺序、表达式索引或部分索引导致行为偏差:
- ✅ 正确(稳定可靠):
INSERT INTO users (id, email) VALUES (1, 'a@b.com')
ON CONFLICT ON CONSTRAINT users_email_key DO NOTHING; - ❌ 风险写法(列名可能对应多个约束,或与索引定义不一致):
ON CONFLICT (email) DO NOTHING —— 若存在UNIQUE (email, tenant_id)索引,此写法不触发冲突
确保目标约束真实存在且生效
幂等性依赖底层唯一约束真正拦截重复数据。需检查:
- 表上是否存在对应
UNIQUE约束或PRIMARY KEY,而非仅靠应用层逻辑保证 - 约束未被
NOT VALID标记(PostgreSQL 中无效约束不参与冲突检测) - 若使用表达式索引(如
LOWER(email)),ON CONFLICT必须用相同表达式,或直接引用该索引名
注意 NULL 值的特殊行为
标准唯一约束中,NULL != NULL,因此多行 email = NULL 不会触发冲突:
- 若业务要求 “邮箱为空也视为唯一”,需改用
UNIQUE NULLS NOT DISTINCT(PostgreSQL 15+)或函数索引(如COALESCE(email, ''))并同步在ON CONFLICT中匹配 - 否则,多次插入
(id=2, email=NULL)会成功多次,不满足业务幂等预期
避免与 RETURNING 混淆“是否插入成功”
DO NOTHING 不返回被忽略的行,但也不报错。如需确认是否真的插入了新行,应结合 RETURNING *:
- 有返回值 → 新增成功
- 无返回值 → 被忽略(已存在)
- 不要依赖语句影响行数(PostgreSQL 中
INSERT ... DO NOTHING成功时始终返回0影响行数)










