最常见原因是表未建PRIMARY KEY或UNIQUE约束;MySQL仅依赖索引检测冲突,无索引则INSERT IGNORE等同普通INSERT,不跳过、不报错、不警告。

INSERT IGNORE 为什么没跳过主键冲突
最常见的情况是:表根本没建主键,或者你误以为某字段是主键,其实只是普通索引或没索引。MySQL 不会主动比对整行是否重复,它只靠 PRIMARY KEY 或 UNIQUE 约束触发冲突检测。执行 SHOW CREATE TABLE your_table;,确认输出里真有 PRIMARY KEY 定义——比如 PRIMARY KEY (`id`)。如果只有 KEY `idx_user` (`user_id`) 这种普通索引,INSERT IGNORE 对它完全无效,行为和普通 INSERT 一模一样。
批量插入时 ROW_COUNT() 返回 0 不代表全失败
一条 INSERT IGNORE INTO t(a,b) VALUES (1,2),(2,3),(1,4); 中,MySQL 是逐行判断的:
- 第一行
(1,2)插入成功 → 计入受影响行数 - 第二行
(2,3)若无冲突也插入成功 - 第三行
(1,4)因a=1冲突被跳过 → 不报错,但不增加计数
最终 ROW_COUNT() 或 mysqli_affected_rows() 返回的是实际新增行数(比如 2),不是“总行数减冲突数”。别看到某行没插进去就怀疑语法错了——它本来就是行级处理。
怎么确认到底是不是主键冲突被忽略了
执行完 INSERT IGNORE 后立刻跑:
SHOW WARNINGS;
如果看到类似这样的输出,说明确实是主键/唯一键冲突被忽略:
Warning 1062 Duplicate entry '123' for key 'PRIMARY'
但如果输出是:
Error 1406 Data too long for column 'name' at row 1
那问题根本不在主键,而是字段超长——INSERT IGNORE 对这种错误完全不管,照样中断。其他常见未被忽略的错误包括:ERROR 1292(非法时间)、向 NOT NULL 字段插 NULL、外键约束失败等。
PHP 中判断是否真被忽略要分三档看返回值
mysqli_affected_rows() 的返回值含义很关键:
-
1:新插入一行(正常) -
0:因主键或唯一键冲突被跳过(这是你期望的“忽略”) -
-1:发生了未被IGNORE覆盖的错误(比如字段超长、类型不匹配)——这才是真出问题了
所以不能只写 if ($affected > 0) 就认为数据进去了。更稳妥的做法是:if ($affected === -1) { /* 处理真实错误 */ },再结合 SHOW WARNINGS 判断是否属于预期中的冲突忽略。
真正容易被忽略的是:没有唯一约束时,INSERT IGNORE 和普通 INSERT 行为一致;而有了约束后,它又只管冲突,不管其他任何错误——这两头都得手动验证,不能凭感觉假设它“应该起作用”。


















