必须在DML语句后立即用SET保存@@ROWCOUNT值,否则被后续语句覆盖;推荐存入局部变量或OUTPUT参数,避免直接依赖其会话级易变状态。

必须在目标语句后立即读取,否则值会被覆盖。 SQL Server 的 @@ROWCOUNT 不是“快照”,而是一个随时被下一条语句重写的会话级状态变量。哪怕中间只加一句 PRINT 'debug' 或 SELECT 1,@@ROWCOUNT 就已变成那条语句的结果(通常是 1 或 0),原始 DML 的影响行数就丢了。
UPDATE/DELETE 后立刻赋值给局部变量
这是最稳妥、最常用的模式:把 @@ROWCOUNT 的值在 DML 执行完的**下一行**就存进 @rows 这类局部变量里,后续逻辑全部基于该变量判断。
常见错误现象:UPDATE 后隔了一行才写 SET @rows = @@ROWCOUNT;或者在 IF 块里直接用 @@ROWCOUNT 判断——此时它已被 IF 本身重置为 0。
- 正确写法示例:
UPDATE Orders SET Status = 'shipped' WHERE OrderID IN (1001, 1002, 1003); SET @rows = @@ROWCOUNT; IF @rows = 0 BEGIN ... END
-
EXEC(@sql)动态执行后,@@ROWCOUNT依然有效,但必须紧接其后读取;不能先PRINT再读 - 如果 DML 可能影响超 21 亿行,改用
SET @rows = ROWCOUNT_BIG(),返回bigint类型
事务中捕获前序操作的影响行数
事务本身(BEGIN TRANSACTION、COMMIT、ROLLBACK)会把 @@ROWCOUNT 重置为 0,所以不能依赖它跨语句“记忆”。关键动作仍得靠变量暂存。
使用场景:需要根据上一步 UPDATE 是否成功来决定是否继续 INSERT 或回滚。
- 务必在每个 DML 后立刻保存:
UPDATE ...; SET @upd_cnt = @@ROWCOUNT;,再INSERT ...; SET @ins_cnt = @@ROWCOUNT; - 不要在
IF @@ROWCOUNT > 0前插入任何非空操作(包括注释以外的语句) -
TRY...CATCH块内也适用,但注意CATCH中的ERROR_MESSAGE()等函数不会改写@@ROWCOUNT,它仍保留TRY块中最后一条语句的值
存储过程输出参数 vs @@ROWCOUNT
对外暴露影响行数时,推荐用 OUTPUT 参数而非让调用方自己查 @@ROWCOUNT。因为调用方无法控制你过程内部是否插入了干扰语句。
例如,一个 sp_UpdateUser 如果内部有日志 INSERT 或 SELECT,外部执行后读 @@ROWCOUNT 得到的其实是日志表的操作结果,不是用户更新数。
- 定义时声明:
CREATE PROCEDURE sp_UpdateUser @UserID INT, @AffectedRows INT OUTPUT - 过程末尾赋值:
SET @AffectedRows = @rows;(其中@rows是你前面存好的值) - 调用方必须显式传入变量接收:
EXEC sp_UpdateUser @UserID=5, @AffectedRows=@cnt OUTPUT - 避免在过程中用
SELECT @AffectedRows = @@ROWCOUNT直接赋值给输出参数——万一 SELECT 前还有别的语句,就错了
真正容易被忽略的是:哪怕只是加了个 SET NOCOUNT ON,它虽不改变 @@ROWCOUNT 的值,但会让客户端收不到“X 行受影响”的消息——这在某些 ORM 或旧版工具里可能触发超时或误判。所以测试时别只看 SSMS 的消息栏,一定用变量捕获并 SELECT 出来验证。










