SQL ON CONFLICT DO NOTHING 的 upsert 幂等性保障写法

5次阅读

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

SQL ON CONFLICT DO NOTHING 的 upsert 幂等性保障写法

sqlON CONFLICT DO NOTHING 本身具备幂等性,但前提是冲突目标(ON CONFLICT ON CONSTRAINTON 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 影响行数)
text=ZqhQzanResources