能,INSERT ... ON CONFLICT DO NOTHING 可跳过冲突行,需显式指定冲突列(如 ON CONFLICT (email))或约束名,否则报错;它原子性处理冲突,避免事务中断,不返回跳过行数。

INSERT ... ON CONFLICT DO NOTHING 能跳过冲突行吗?
能,这是 PostgreSQL 9.5+ 最直接、最安全的方案。它不是“忽略错误后继续”,而是主动把冲突当作正常流程处理,避免事务中断。
常见错误是误用 ON CONFLICT 但没指定冲突目标(比如漏写 ON CONFLICT (column_name)),结果触发 ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification。
- 必须明确写出冲突列或约束名:用
ON CONFLICT (email)(列名)或ON CONFLICT ON CONSTRAINT users_email_key(约束名) - 如果表有多个唯一约束(如
email和phone),只声明其中一个,其他冲突仍会报错 -
DO NOTHING不会返回被跳过的行数,想统计需额外用RETURNING配合临时表或 CTE
批量插入时用 VALUES 还是 FROM 子查询?
优先用 VALUES 列表,语义清晰、性能好;FROM 子查询适合数据来自其他表或需复杂过滤的场景。
注意:当插入大量行(比如上万)时,单条含几千 VALUES 的语句可能触发内存或协议限制,PostgreSQL 默认 max_locks_per_transaction 或网络缓冲可能成为瓶颈。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 稳妥做法是分批插入,每批 100–1000 行,用循环或客户端逻辑控制
- 不要在
VALUES里混用不同数据类型(如'1'和1),否则可能触发隐式转换失败 - 若从 CSV 导入,
COPY ... ON CONFLICT不支持,得先导入临时表,再用INSERT ... SELECT ... ON CONFLICT
为什么不能用 INSERT IGNORE 或 REPLACE INTO?
PostgreSQL 没有 INSERT IGNORE(MySQL)或 REPLACE INTO(SQLite/MySQL)语法。强行模仿——比如先 DELETE 再 INSERT——会破坏原子性,且在高并发下极易出错。
典型翻车场景:两个事务同时发现某行不存在,都执行 INSERT,结果一个成功一个因唯一冲突失败,但若你用 DELETE + INSERT 代替,可能删掉对方刚插入的行。
-
ON CONFLICT DO UPDATE是替代REPLACE的正确方式,但要注意EXCLUDED别名的使用(如SET updated_at = EXCLUDED.updated_at) - 试图用
WHERE NOT EXISTS包裹INSERT也无效:在并发下,NOT EXISTS检查和INSERT不是一次原子操作 - 触发器里做去重?不可靠。触发器无法阻止唯一索引报错,且增加开销
ON CONFLICT 在 UPSERT 场景下容易被忽略的细节
真正难的不是语法,而是理解“冲突判定时机”和“约束覆盖范围”。例如,唯一索引允许 NULL,但 NULL != NULL,所以多行 NULL 值不会触发冲突——这常被当成 bug,其实是 SQL 标准行为。
- 复合唯一约束(如
UNIQUE (tenant_id, code))下,只有两列值完全相同时才冲突;单独一列重复不触发 - 部分索引(
CREATE UNIQUE INDEX ... WHERE status = 'active')也会被ON CONFLICT识别,但必须确保插入数据满足WHERE条件才会检查该索引 - 如果用了
DEFERRABLE唯一约束,ON CONFLICT默认不生效,得配合SET CONSTRAINTS ... DEFERRED才行,极少需要

















