代码之家  ›  专栏  ›  技术社区  ›  Pure.Krome

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

  •  22
  • Pure.Krome  · 技术社区  · 15 年前

    在插入或更新记录之后,我只想更新该表上的一个字段。

    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 回复  |  直到 9 年前
        1
  •  52
  •   Anssssss    9 年前

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

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

    MSDN link

        2
  •  33
  •   TT.    8 年前

    1. 禁用触发器递归 :

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

      ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF
      GO
      
    2. 使用 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
      
        3
  •  8
  •   Remus Rusanu    15 年前
    ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF
    

    RECURSIVE_TRIGGERS { ON | OFF }

    打开 允许递归触发AFTER触发器。

    不允许只直接递归触发AFTER触发器。到 在触发器之后,设置嵌套 sp U配置。

    当递归触发器设置为“关”时,仅阻止直接递归。 要禁用间接递归,请 0的服务器选项。

    此选项的状态可以通过检查 sys.databases目录视图或 的IsRecursiveTriggersenable属性

        4
  •  6
  •   Colin    7 年前

    TRIGGER_NESTLEVEL 可用于防止特定触发器的递归,但必须将触发器的对象id传递到函数中。否则,当另一个触发器进行插入或更新时,也会阻止触发器触发:

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

    MSDN

    如果未指定任何参数,TRIGGER\u nestlever将返回

    参考: Avoiding recursive triggers

        5
  •  5
  •   Pure.Krome    15 年前

    我想我明白了:)

    此外,我还让它处理多个更改行->我总是用触发器忘记这一点。

    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
    
        6
  •  1
  •   DVK    15 年前

    可以有一个单独的可为空列,指示是否设置了UniqueTitle。

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

        7
  •  0
  •   HLGEM    9 年前

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

    我还将考虑是否最好将执行递归的触发器组合成一个触发器。