INSERT IGNORE用于主键或唯一索引冲突时静默跳过单行,不报错、不中断后续插入;必须预先建立对应UNIQUE或PRIMARY KEY约束,否则无效,且仅通过ROW_COUNT()判断是否被忽略。

MySQL 用 INSERT IGNORE 跳过冲突并继续
INSERT IGNORE 是最轻量的容错方案,适用于主键或唯一索引冲突时静默跳过单条记录,不报错、不中断后续插入。
必须提前在表中为冲突字段(如 id 或 email)建好 UNIQUE 或 PRIMARY KEY 约束,否则 INSERT IGNORE 完全无效,所有数据照插不误。
常见错误现象:执行后部分数据没进去,但没报错也没警告——大概率是约束没建,或建在了错误字段上。
-
INSERT IGNORE不会返回哪一行失败,只靠事后查ROW_COUNT()或比对源数据与目标表数量来发现丢失 - 不能捕获错误码或具体冲突字段,适合“能插就插、不关心谁被跳过”的场景
- 注意 SQL 模式:若启用了
STRICT_TRANS_TABLES,某些类型转换错误仍会中断整批
MySQL 用 ON DUPLICATE KEY UPDATE 显式处理冲突
当需要“存在则更新、不存在则插入”时,ON DUPLICATE KEY UPDATE 是更可控的选择。它不跳过,而是把冲突行转为更新操作。
关键点在于:它只响应主键或唯一索引冲突;其他错误(如字段超长、NULL 插入 NOT NULL 字段)仍会报错并中断整批。
- 语法中
VALUES(column_name)表示取本次 INSERT 中该字段的值,不是当前行旧值 - 批量插入时,每条
VALUES行都独立触发检查,失败只影响本行,其余继续执行 - 返回值可区分:影响行数为 1 表示纯插入,为 2 表示先删后插(仅限
REPLACE INTO),而ON DUPLICATE KEY UPDATE影响行为 1(插入)或 2(更新)
Oracle FORALL + SAVE EXCEPTIONS 捕获具体失败位置
Oracle 原生支持批量错误定位:FORALL ... SAVE EXCEPTIONS 必须配合 BULK COLLECT 和异常块使用,不能放在普通 LOOP 里。
执行后若部分失败,会抛出固定错误码 -24381,再通过 SQL%BULK_EXCEPTIONS 数组拿到每个失败项的 ERROR_INDEX(原数组下标)和 ERROR_CODE(如 1 表示主键冲突)。
- 必须显式声明集合类型,且
FORALL i IN INDICES OF xxx写法比1..xxx.COUNT更安全(跳过空元素) -
SAVE EXCEPTIONS不改变事务状态:成功的记录已写入,失败的未写入;是否回滚由你控制 - 别漏掉
EXCEPTION WHEN OTHERS THEN ... ELSE RAISE;,否则其他非批量错误会被吞掉
通用策略:分块 + 事务 + 单条重试
当数据库不支持原生批量容错(如某些 PostgreSQL 版本早期),或你需要精确知道哪条数据、哪个字段出错时,分块+单条事务是最稳妥的兜底方式。
把 1000 条数据拆成每块 50–100 条,在循环内开启事务、单条 INSERT、try/catch 捕获异常、记录失败详情、continue,最后统一提交。
- 性能明显低于原生批量,但调试友好:日志能直接输出
data[237].email = 'xxx'格式错误 - 注意连接池压力:1000 条 = 1000 次 round-trip,高并发下可能打满连接数
- 某些 ORM(如 ThinkPHP 的
save())默认不开启事务,需手动Db::startTrans()包裹
真正容易被忽略的是约束前提和错误分类:没建唯一索引,INSERT IGNORE 就是摆设;没配 STRICT 模式,MySQL 可能默默截断字符串却不报错;而 Oracle 的 SAVE EXCEPTIONS 只捕获 DML 错误,PL/SQL 运行时异常(比如除零)照样中断整个块。










