为什么即使基础表没有改变我的触发器也会被调用?

Bra*_*ann 0 trigger sql-server sql-server-2008-r2

我有一个非常基本的触发器定义如下:

CREATE TRIGGER dbo.foo
ON dbo.bar
FOR INSERT, UPDATE, DELETE
AS
   ...
Run Code Online (Sandbox Code Playgroud)

我还有一个存储过程,它在某些时候会做这样的事情:

Insert into dbo.bar (a) select a from @bar
Run Code Online (Sandbox Code Playgroud)

当@bar 包含一些数据时,我的触发器会按预期调用,但当它不包含时,即使 dbo.bar 显然没有更改,也会调用触发器。

SQL 探查器和 sys.dm_exec_trigger_stats 的内容都证实了这一点。

SQL Profiler 报告占用空间非常低(3 次读取,0 次写入),但由于我有很多触发器,因此这种情况经常发生,并且最终得到了重要的 total_reads 数字。

关于为什么会发生这种情况的任何提示?有没有办法改变这种行为?

ype*_*eᵀᴹ 7

这是语句触发器的预期行为。每次执行一条语句(INSERT在本例中)和一次(每条语句,而不是每行)时,它都会执行一次。其他 DBMS(如 Oracle)中的行为类似。

MSDN 在CREATE TRIGGER页面的第一段中说:

无论是否有任何表行受到影响,当触发任何有效事件时都会触发这些触发器。

我认为没有办法改变这一点。有一些方法可以检查 - 在触发器内部,一旦触发 - 是否有受影响的行并相应地采取行动。

例如,您可以使用

IF ( @@ROWCOUNT = 0 )
  RETURN;
Run Code Online (Sandbox Code Playgroud)

或者在存在的情况下更强大的替代方案MERGE

IF NOT EXISTS(SELECT *
              FROM   DELETED)
   AND NOT EXISTS(SELECT *
                  FROM   INSERTED)
  RETURN; 
Run Code Online (Sandbox Code Playgroud)