Как предотвратить повторение триггера базы данных? - PullRequest
24 голосов
/ 07 октября 2009

У меня есть следующий триггер для таблицы для базы данных 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 ]

49 голосов
/ 01 июня 2015

Не уверен, что это больше относится к вопросу OP, но если вы пришли сюда, чтобы узнать, как предотвратить рекурсию или взаимную рекурсию в триггере, вы можете проверить это следующим образом:

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

MSDN ссылка

33 голосов
/ 07 октября 2009

Я вижу три возможности:

  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
    
9 голосов
/ 07 октября 2009
ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF

RECURSIVE_TRIGGERS {ON | OFF}

ON Рекурсивное срабатывание триггеров AFTER разрешено.

OFF Запрещено только прямое рекурсивное срабатывание триггеров AFTER. к также отключить косвенную рекурсию ПОСЛЕ триггеров, установите вложенные запускает опцию сервера на 0 с помощью sp_configure.

Только прямая рекурсия предотвращается, когда RECURSIVE_TRIGGERS установлен в OFF. Чтобы отключить косвенную рекурсию, вы необходимо также установить вложенные триггеры опция сервера до 0.

Статус этой опции можно определить, изучив Столбец is_recursive_triggers_on в представление каталога sys.databases или IsRecursiveTriggersEnabled свойство функция DATABASEPROPERTYEX.

5 голосов
/ 02 ноября 2017

TRIGGER_NESTLEVEL может использоваться для предотвращения рекурсии определенного триггера, но важно передать идентификатор объекта триггера в функцию. В противном случае вы также предотвратите срабатывание триггера, если другой триггер выполняет вставку или обновление:

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

С MSDN :

Если параметры не указаны, TRIGGER_NESTLEVEL возвращает итог количество триггеров в стеке вызовов. Это включает себя.

Ссылка: Предотвращение рекурсивных триггеров

5 голосов
/ 07 октября 2009

Мне кажется, я понял :) 1001 *

Когда заголовок обновляется (читай: вставляется или обновляется), обновите уникальную тему. Когда триггер запускается второй раз, поле уникальной темы обновляется, поэтому оно останавливается и покидает триггер.

Кроме того, я сделал так, чтобы он обрабатывал НЕСКОЛЬКО строк, которые меняются -> Я всегда забываю об этом с помощью триггеров.

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
1 голос
/ 07 октября 2009

Вы можете иметь отдельный столбец NULLABLE, указывающий, был ли установлен UniqueTitle.

Установите для него истинное значение в триггере, и пусть триггер ничего не делает, если его значение истинно в INSERTED

0 голосов
/ 23 марта 2015

Для полноты картины я добавлю несколько вещей. Если у вас есть определенный триггер после запуска, который вы хотите запустить только один раз, вы можете настроить его на последний запуск с помощью sp_settriggerorder.

Я бы также подумал, не лучше ли объединить триггеры, выполняющие рекурсию, в один триггер.

...