必须用ROW_COUNT()查,需在同连接中紧接多行INSERT后调用,返回实际写入的行数(非语句行数或自增ID数);中间插入SELECT等语句会导致返回-1或错误值。

MySQL多行INSERT后怎么拿到实际插入的行数?
直接查 ROW_COUNT() 就行,但必须在同个连接、紧接 INSERT 之后调用。它返回的是“被影响的行数”,对 INSERT 来说就是成功写入的记录条数(含重复键被忽略或更新的情况)。注意:不是自增ID数量,也不是语句里写的行数。
常见错误是执行完 INSERT 后又发了其他语句(比如 SELECT 或日志打印),再查 ROW_COUNT()——这时值已丢失或变成上一条语句的结果。
- PHP PDO 示例:
$pdo->exec("INSERT INTO t(a) VALUES (1),(2),(3)"); echo $pdo->lastInsertId(); // 错!这是第一个生成的ID;要用 $pdo->getAttribute(PDO::ATTR_ROW_COUNT) - MySQL CLI 或脚本中,必须紧跟:
INSERT INTO users(name) VALUES ('a'),('b'),('c');<br>SELECT ROW_COUNT(); - 如果表有
ON DUPLICATE KEY UPDATE,ROW_COUNT()对“新插入”返回 1,对“发生更新”返回 2——这个细节常被忽略
为什么用VALUES()一次插多行,而不是循环单条INSERT?
性能差距极大。单条 INSERT 每次都要走解析、权限检查、事务日志刷盘等完整流程;而多值 INSERT INTO t(col) VALUES (1),(2),(3) 是单次语句,网络往返少、锁持有时间短、日志合并写入更高效。
但要注意长度限制:max_allowed_packet 决定了单条语句最大体积,默认通常 4MB。如果批量插入超限,会报错 Packets larger than max_allowed_packet are not allowed。
- 安全上限建议控制在 1000 行以内(具体看单行数据大小)
- 不要拼接超长字符串构造
VALUES,容易触发 SQL 注入或内存溢出 - 替代方案:用
LOAD DATA INFILE导入大文件,但需要文件权限且不支持参数化
INSERT ... ON DUPLICATE KEY UPDATE 场景下 ROW_COUNT() 怎么解读?
这个语句的 ROW_COUNT() 返回值有三种可能:0(无变更)、1(新插入)、2(发生了更新)。关键看是否命中唯一键/主键冲突。
例如:INSERT INTO t(id, name) VALUES (1,'x'),(2,'y') ON DUPLICATE KEY UPDATE name=VALUES(name),若 id=1 已存在、id=2 不存在,则最终 ROW_COUNT() 返回 3(1 行更新 + 1 行插入 = 2?不对——MySQL 把每行单独计数,所以是 1+2=3)。
- 严格来说,返回值 = 插入行数 × 1 + 更新行数 × 2
- 想区分插入/更新各自数量?只能提前
SELECT COUNT(*)查是否存在,或改用REPLACE INTO(但会删再插,自增ID跳变) - 应用层别依赖
ROW_COUNT()值做业务分支逻辑,尤其涉及并发写入时,结果不可靠
用 prepared statement 执行多行插入时,ROW_COUNT() 还准吗?
准,但必须确保预处理语句只执行一次,且后续没执行其他语句。PDO 和 MySQLi 都支持获取影响行数,但接口不同:
- PDO:
$stmt = $pdo->prepare("INSERT INTO t(x) VALUES (?)"); $stmt->execute([1]); $stmt->execute([2]); echo $stmt->rowCount(); // 注意:这是最后一次 execute 的影响数,不是累计 - 正确做法是拼成多值语句:
$stmt = $pdo->prepare("INSERT INTO t(x) VALUES (?,?);"); $stmt->execute([1,2]); echo $stmt->rowCount(); - MySQLi 面向对象方式:
$mysqli->query("INSERT ..."); echo $mysqli->affected_rows;—— 这个值和ROW_COUNT()一致
最易被忽略的一点:很多 ORM(如 Laravel Eloquent)的 insert() 方法默认不返回行数,它底层可能拆成多条语句或用事务包装,此时 ROW_COUNT() 拿到的是最后那条语句的结果,而非整个批次。


















