如何防止数据库触发器递归?

时间:2009-10-07 04:02:36

标签: sql sql-server tsql sql-server-2008 triggers

我在SQL Server 2008数据库的表上有以下触发器。它正在递归,所以我需要阻止它。

插入或更新记录后,我试图只更新该表上的单个字段。

这是触发器:

ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
   ON  [dbo].[tblMedia]
   BEFORE INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    DECLARE @IdMedia INTEGER,
        @NewSubject NVARCHAR(200)   

    SELECT @IdMedia = IdMedia, @NewSubject = Title
    FROM INSERTED

    -- Now update the unique subject field.
    -- NOTE: dbo.CreateUniqueSubject is my own function. 
    --       It just does some string manipulation.
    UPDATE tblMedia
    SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
                      CAST((IdMedia) AS VARCHAR(10))
    WHERE tblMedia.IdMedia = @IdMedia
END

有谁能告诉我如何防止触发器的插入再次触发另一个触发器?

7 个答案:

答案 0 :(得分:48)

不确定它是否与OP的问题相关,但是如果你来这里是为了找出如何防止在触发器中发生递归或相互递归,你可以这样测试:< / p>

IF TRIGGER_NESTLEVEL() <= 1/*this update is not coming from some other trigger*/

MSDN link

答案 1 :(得分:32)

我看到三种可能性:

  1. 禁用触发器递归

    这将阻止触发器触发另一个触发器或再次调用自身。为此,请执行以下命令:

    ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF
    GO
    
  2. 使用触发器INSTEAD OF UPDATE,INSERT

    使用INSTEAD OF触发器可以控制任何正在更新/插入的列,甚至可以在调用命令之前进行替换。

  3. 通过阻止使用IF UPDATE

    来控制触发器

    如果触发器调用自身,测试列将以合理的准确度告诉您。为此,请使用IF UPDATE()子句,如:

    ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate]
       ON  [dbo].[tblMedia]
       FOR INSERT, UPDATE
    AS
    BEGIN
        SET NOCOUNT ON
        DECLARE @IdMedia INTEGER,
            @NewSubject NVARCHAR(200)   
    
        IF UPDATE(UniqueTitle)
          RETURN;
    
        -- What is the new subject being inserted?
        SELECT @IdMedia = IdMedia, @NewSubject = Title
        FROM INSERTED
    
        -- Now update the unique subject field.
        -- NOTE: dbo.CreateUniqueSubject is my own function. 
        --       It just does some string manipulation.
        UPDATE tblMedia
        SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
                          CAST((IdMedia) AS VARCHAR(10))
        WHERE tblMedia.IdMedia = @IdMedia
    END
    

答案 2 :(得分:9)

ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF
  

RECURSIVE_TRIGGERS { ON | OFF }

     

ON 允许AFTER触发器的递归触发。

     

OFF 不允许直接递归触发AFTER触发器。至   还禁用间接递归   AFTER触发器,设置嵌套   使用将服务器选项触发为0   sp_configure的。

     

当RECURSIVE_TRIGGERS设置为OFF时,仅阻止直接递归。   要禁用间接递归,请执行此操作   还必须设置嵌套触发器   服务器选项为0。

     

此选项的状态可以通过检查来确定   is_recursive_triggers_on列中的   sys.databases目录视图或   IsRecursiveTriggersEnabled属性   DATABASEPROPERTYEX函数。

答案 3 :(得分:5)

我想我明白了:)

当标题获得“更新”(读取:插入或更新)时,请更新唯一主题。当触发器第二次运行时,uniquesubject字段会更新,因此它会停止并离开触发器。

另外,我已经让它处理了更改的MULTIPLE行 - &gt;我总是忘记触发器。

ALTER TRIGGER [dbo].[tblMediaAfterInsert] 
   ON  [dbo].[tblMedia]
   FOR INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    -- If the Title is getting inserted OR updated then update the unique subject.
    IF UPDATE(Title) BEGIN
        -- Now update all the unique subject fields that have been inserted or updated.
        UPDATE tblMedia 
        SET UniqueTitle = dbo.CreateUniqueSubject(b.Title) + 
                          CAST((b.IdMedia) AS VARCHAR(10))
        FROM tblMedia a
            INNER JOIN INSERTED b on a.IdMedia = b.IdMedia
    END
END

答案 4 :(得分:5)

TRIGGER_NESTLEVEL可用于防止特定触发器的递归,但将触发器的对象id传递给函数非常重要。否则,当另一个触发器进行插入或更新时,您还将阻止触发器触发:

   IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.mytrigger')) > 1
         BEGIN
             PRINT 'mytrigger exiting because TRIGGER_NESTLEVEL > 1 ';
             RETURN;
     END;

来自MSDN

  

如果未指定参数,则TRIGGER_NESTLEVEL返回总计   调用堆栈上的触发器数量。这包括它自己。

参考: Avoiding recursive triggers

答案 5 :(得分:1)

您可以使用单独的NULLABLE列来指示是否已设置UniqueTitle。

在触发器中将其设置为true值,如果在“INSERTED”中值为true,则触发器不执行任何操作

答案 6 :(得分:0)

为了完整起见,我将添加一些内容。如果您有一个特定的触发后只需要运行一次,则可以使用sp_settriggerorder将其设置为最后一次运行。

我还会考虑是否最好将正在进行递归的触发器合并到一个触发器中。