@@ROWCOUNT仅反映紧邻上一条语句的影响行数,必须在SET NOCOUNT ON等语句前检查;UPDATE无实际变更或WHERE不匹配时其值为0;嵌套调用会覆盖它,需立即存入局部变量。

SET NOCOUNT ON 必须放在 @@ROWCOUNT 检查之后
触发器里一上来就写 SET NOCOUNT ON,@@ROWCOUNT 几乎必然为 0。因为 SET NOCOUNT ON 本身是一条语句,它会覆盖上一条语句(比如 INSERT/UPDATE)留下的行数记录。
常见错误写法:
ALTER TRIGGER [trg_Orders_Insert] ON [dbo].[Orders] AFTER INSERT AS BEGIN SET NOCOUNT ON; -- ← 这行一执行,@@ROWCOUNT 就被清零了 IF @@ROWCOUNT = 0 RETURN; -- ← 总是成立,逻辑直接退出 ... END
-
@@ROWCOUNT只反映「紧邻上一条」语句的影响,不是整个触发器上下文的累计值 - 任何赋值、声明、
SET语句都会改写它,包括SET NOCOUNT ON、DECLARE @x INT、甚至空行后的注释(只要它被解析为语句) - 正确顺序:先用
IF @@ROWCOUNT = 0 RETURN做快速退出判断,再开SET NOCOUNT ON
UPDATE 无实际变更时 @@ROWCOUNT = 0,但触发器仍会执行
SQL Server 不像 MySQL 8.0+ 那样静默跳过触发器,但它会让 @@ROWCOUNT 返回 0 —— 即使语句语法合法、表存在、权限够,也即使你看到“1 row affected”这种客户端提示。
典型场景:
-
UPDATE Products SET Price = Price WHERE ID = 123→ 字段值没变,@@ROWCOUNT为 0 -
UPDATE Orders SET Status = 'Shipped' WHERE OrderID = 999 AND Status = 'Shipped'→ WHERE 条件不匹配任何行,@@ROWCOUNT也为 0 - 别依赖客户端返回的 “X rows affected”,那只是语句执行结果;
@@ROWCOUNT才是你在触发器里能拿到的真实影响行数
触发器嵌套或调用存储过程会覆盖 @@ROWCOUNT
如果触发器里执行了另一个存储过程,而那个过程内部有 SELECT、UPDATE 或 INSERT,它的执行会立刻改写 @@ROWCOUNT。你原来想检查主 DML 影响的行数,可能已经被覆盖成子过程里某条语句的结果。
- 必须在调用任何外部逻辑前,把
@@ROWCOUNT保存到局部变量:DECLARE @rc INT = @@ROWCOUNT -
EXEC语句本身不改变@@ROWCOUNT,但其内部执行的每一条语句都会 - 尤其注意游标操作:
DECLARE CURSOR和FETCH都会把@@ROWCOUNT设为 1,哪怕没取到数据
@@ROWCOUNT 在事务回滚后仍保留原值
很多人误以为 ROLLBACK 会让 @@ROWCOUNT 归零,其实不会。@@ROWCOUNT 记录的是“上一条语句尝试影响的行数”,和最终是否提交无关。
-
UPDATE Stock SET Qty = Qty - 1 WHERE ID = 5; SELECT @@ROWCOUNT;→ 返回 1,哪怕后续ROLLBACK - 这意味着不能靠
@@ROWCOUNT判断“数据是否真的被改了”,只能判断“有没有行被语句选中并尝试修改” - 若需确认变更生效,得配合事务状态(如
XACT_STATE())或显式查表
触发器里用 @@ROWCOUNT 最容易忽略的点,是它根本不“记忆”——它只忠实地反映前一条语句的副作用,且毫无容错余地。任何中间插入的语句、任何嵌套调用、甚至一个没注意的 SET,都会让它失效。别把它当状态变量用,只当它是瞬时快照。










