MySQL 中 DELETE 后立刻获知删除行数应调用 ROW_COUNT(),它返回上一条 DML 实际变更行数;PostgreSQL 则需用 RETURNING 子句或客户端 API 获取影响行数。

MySQL 里 DELETE 后怎么立刻知道删了几行
直接查 ROW_COUNT(),不是SELECT COUNT(*) 回查,也不是看客户端返回的“Query OK”模糊提示。它返回的是上一条DELETE(或UPDATE/INSERT)实际变更的行数,精确到个位。
常见错误是执行完 DELETE FROM t WHERE x = 1 就以为完事了,结果发现没删成——可能条件写错、事务没提交、或者根本没匹配到数据。这时候 ROW_COUNT() 返回0,就是最直接的信号。
- 必须在同一线程 / 连接中紧接
DELETE之后调用,跨语句或换连接就失效 - 存储过程中可用
SELECT ROW_COUNT();应用层如 Python 用cursor.rowcount(注意:部分驱动需在execute()后立即读,不能先fetch) - 如果
DELETE带LIMIT,ROW_COUNT()返回的是实际删除行数,不是LIMIT值
PostgreSQL 没有 ROW_COUNT() 函数,怎么办
PostgreSQL 不用函数,靠 RETURNING 子句或客户端 API 获取影响行数。它不提供类似 MySQL 的会话级计数器,所以得从执行动作本身拿反馈。
典型场景:你想确认DELETE FROM orders WHERE status = 'canceled' AND created_at 到底清了几单,又不想再跑一遍 <code>SELECT COUNT(*)——那就在删的时候加RETURNING id,然后数结果集行数。
- 简单确认数量:用
DELETE …… RETURNING 1,再取结果集长度(比RETURNING *轻量) - 想同时审计删了哪些数据:用
RETURNING id, updated_at,但要注意大结果集可能 OOM - 应用层如 psycopg2 中,
cursor.rowcount在DELETE后即有效,值就是影响行数,无需额外查询 - 注意:如果
DELETE被触发器拦截或改写(比如转成 UPDATE),rowcount仍反映原始语句声明的影响行数,不一定等于最终物理删除数
ROW_COUNT()在事务里会受什么干扰
它只反映最近一条修改语句的结果,和事务是否 COMMIT 无关。但如果你在事务里连着执行多条 DML,中间穿插了 SELECT,那ROW_COUNT() 可能被覆盖或归零——尤其在某些客户端驱动里,SELECT会重置这个值。
- MySQL 中:
SELECT语句本身不改变ROW_COUNT(),但执行后再次调用ROW_COUNT()会返回-1(表示无意义),不是上一条DELETE的值 - 务必把
SELECT ROW_COUNT()放在DELETE之后、其他任何语句之前 - 在存储过程里,避免用
IF ROW_COUNT() = 0 THEN ……这种逻辑包裹在SELECT之后,容易误判 - 如果用了
SAVEPOINT并回滚部分操作,ROW_COUNT()不会回退——它只记最后执行成功的那条 DML
为什么不能依赖客户端返回的“Affected rows”字符串
很多命令行工具(如mysql CLI)或 ORM 日志里会打印类似Query OK, 42 rows affected,但这只是客户端对服务端响应包的解析结果,不可编程依赖。真正要写进逻辑里的判断,必须通过数据库原生机制获取。
- CLI 输出可能被截断、格式化或静默丢弃(比如开启
--silent) - 某些 ORM(如 Django ORM)的
delete()方法返回的是{'deleted': N}字典,底层其实也是调了ROW_COUNT()或等效 API,但你不能假设所有 ORM 都这么干 - 网络中断、连接复用、代理层(如 ProxySQL)可能篡改或丢失响应中的影响行数字段
- 唯一可靠路径:在 SQL 层用
ROW_COUNT()(MySQL)或靠RETURNING/ 驱动rowcount(PostgreSQL),别信日志里印出来的那行字