直接查row_count()(mysql)、@@rowcount(sql server)、sql%rowcount(pl/sql)或驱动rowcount属性,必须紧接在insert后执行,不可隔语句、跨连接或依赖select结果长度。

直接查 ROW_COUNT()(MySQL)、@@ROWCOUNT(SQL Server)、sql%rowcount(PL/SQL)或驱动暴露的 rowcount 属性(Python/Go),但必须紧接在 INSERT 之后执行,不能隔语句、不能跨连接、不能依赖 SELECT 结果长度。
MySQL:用 SELECT ROW_COUNT() 紧跟 INSERT
MySQL 不会在 INSERT 执行后自动返回影响行数,得手动问。最可靠方式是同一连接中,INSERT 后立刻执行 SELECT ROW_COUNT():
INSERT INTO users (name) VALUES ('Alice'), ('Bob');-
SELECT ROW_COUNT();→ 返回2
注意:ROW_COUNT() 返回的是“实际插入行数”,不是匹配数;如果用了 INSERT ... ON DUPLICATE KEY UPDATE,它返回的是插入 + 更新的总行数(例如 1 行已存在被更新,则返回 1);若连接被复用或中间穿插了 SELECT,值会被重置。
SQL Server:用 SELECT @@ROWCOUNT 或 ROWCOUNT_BIG()
SQL Server 的 @@ROWCOUNT 是会话级全局变量,INSERT 后立即读取即可:
-
INSERT INTO users (name) VALUES ('Charlie'); SELECT @@ROWCOUNT;→ 返回1 - 若影响行数可能超 21 亿,改用
SELECT ROWCOUNT_BIG() - 任何中间语句(哪怕只是
DECLARE @x INT)都会覆盖@@ROWCOUNT,所以必须“紧挨着”
ADO.NET 中常封装成 ExecuteNonQuery() 的返回值,本质就是取 @@ROWCOUNT;但若 SQL 批处理里含多个语句,需确保它是最后一条 DML 后的首个查询。
PostgreSQL:靠驱动解析 CommandComplete 消息
PostgreSQL 服务端不提供会话函数,影响行数由客户端驱动从协议层提取。例如:
- psycopg2 中:
cursor.execute("INSERT INTO users ..."); print(cursor.rowcount) - 必须在
execute()后立刻读rowcount,下一次execute()就会覆盖 - 别用
RETURNING *的结果长度代替 —— 它返回的是“被操作的行”,不是“被变更的行”;UPDATE 值未变时,RETURNING仍返回该行,但协议报的受影响行数可能是 0
底层是解析 PostgreSQL 协议的 CommandComplete 消息体里的数字,不是数据库函数调用,所以 ORM 如 SQLAlchemy 需显式访问 result.rowcount,且对 SELECT 返回 -1。
SQLite / Python / Go:驱动行为差异大,别信默认值
SQLite 的 C API 本身区分 sqlite3_changes()(本次连接变更)和 sqlite3_total_changes()(进程级累计),但各语言驱动封装不一致:
- Python
sqlite3模块:cursor.rowcount对 INSERT/UPDATE/DELETE 一般可用,但对无参数语句如INSERT INTO t VALUES ()可能返回-1而非1 - Go 的
RowsAffected()在部分 SQLite 驱动里直接返回-1,因为底层未实现或未启用 - SELECT 永远返回
-1,这不是 bug,是设计如此
真正稳定的方式是:执行前调用 sqlite3_total_changes() 记个基线,执行后再取一次相减;但要注意多线程下该函数是进程级的,跨连接不安全。
最容易被忽略的一点:所有数据库的“影响行数”都只反映**本连接、本语句、本事务内**的实际变更,和 WHERE 条件匹配了多少行是两回事;UPDATE 字段值未变时,多数引擎默认不计为“影响”,这个逻辑在迁移或调试时经常引发误判。











