insert ignore 未跳过重复数据的主因是缺失unique key或primary key约束;mysql仅通过索引检测冲突,若email字段无唯一索引,则相同email可重复插入;需用show create table检查索引,有重复值须先清理再建唯一索引。

INSERT IGNORE 为什么没跳过重复数据
最常见原因是目标字段根本没建 UNIQUE KEY 或 PRIMARY KEY。MySQL 不会主动比对整行内容是否重复,它只靠索引触发冲突检测。比如你想按 email 去重,但表里只有 id 是主键、email 没加唯一约束,那两条相同 email 的记录照样插入成功。
检查方式:运行 SHOW CREATE TABLE your_table;,看输出里有没有类似 UNIQUE KEY `uniq_email` (`email`) 的定义。
- 已有重复值?先清理再建索引:
DELETE t1 FROM user t1 INNER JOIN user t2 WHERE t1.id - 补加唯一索引:
ALTER TABLE user ADD UNIQUE KEY uniq_email (email); - 复合去重场景(如用户+事件类型):
ALTER TABLE event_log ADD UNIQUE KEY uniq_user_event (user_id, event_type);
哪些错误会被忽略,哪些仍报错
INSERT IGNORE 只把 Duplicate entry 类错误降级为警告,其他所有错误照常中断执行。
- 被忽略的:主键或唯一索引冲突 → 返回值为
0,SHOW WARNINGS显示Duplicate entry 'xxx' for key 'PRIMARY' - 仍报错的:
ERROR 1406(字段超长)、ERROR 1292(非法时间格式如'2026-02-30')、向NOT NULL字段插NULL、外键失败、类型不匹配 - 批量插入时,某一行冲突被跳过,其余行不受影响;但只要有一行触发非冲突类错误(如超长),整条语句就中断
PHP 中怎么判断 INSERT IGNORE 是否“正常跳过”还是“真出错了”
别只看 mysqli_affected_rows() 是否大于 0 —— 它返回 0 表示“因唯一冲突被跳过”,这是预期行为;返回 -1 才代表发生了未被 IGNORE 覆盖的错误(比如字段超长)。
- 稳妥做法:先获取返回值,再立即执行
SHOW WARNINGS查看是否有Duplicate entry提示 - 示例逻辑:
if ($affected === -1) { /* 真错误,需处理 */ } elseif ($affected === 0) { /* 正常跳过,可忽略或记录 */ } else { /* 新增成功 */ } - 注意:事务中被忽略的语句仍算已执行,会影响
LAST_INSERT_ID(),且不会回滚整条语句
别和 ON DUPLICATE KEY UPDATE 混用
INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 是互斥语法,不能同时出现。写成 INSERT IGNORE INTO t (...) VALUES (...) ON DUPLICATE KEY UPDATE ... 会直接报语法错误。
- 纯跳过重复 → 用
INSERT IGNORE - 存在就更新部分字段 → 改用
INSERT ... ON DUPLICATE KEY UPDATE - 想彻底覆盖旧记录(接受 ID 重置、触发器执行等副作用)→ 用
REPLACE INTO,但它不是IGNORE的增强版,而是完全不同的语义
真正容易被忽略的是:NULL 在唯一索引中是允许重复的,所以 INSERT IGNORE 对多个 NULL 值不生效;还有大小写敏感性依赖排序规则(utf8mb4_bin vs utf8mb4_unicode_ci),看似相同的字符串可能被判定为不重复。











