Подтвердить что ты не робот

Как предотвратить рекурсирование триггера базы данных?

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

Может ли кто-нибудь сказать мне, как я могу предотвратить запуск триггерной вставки с другого триггера снова?

4b9b3361

Ответ 1

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

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

MSDN ссылка

Ответ 2

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

  • Отключить рекурсию триггера:

    Это предотвратит запуск триггера для вызова другого триггера или повторного вызова. Для этого выполните следующую команду:

    ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF
    GO
    
  • Использовать триггер INSTEAD OF UPDATE, INSERT

    Используя триггер INSTEAD OF, вы можете контролировать любой обновляемый/вставленный столбец и даже заменять перед вызовом команды.

  • Управляйте триггером, предотвращая использование 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

ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF

RECURSIVE_TRIGGERS {ON | OFF}

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

ВЫКЛ Разрешено только прямое рекурсивное включение триггеров AFTER. к также отключить косвенную рекурсию ПОСЛЕ триггеров установите вложенный параметр сервера триггеров равен 0, используя sp_configure.

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

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

Ответ 4

Я думаю, что получил:)

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

Кроме того, я заставил его обрабатывать MULTIPLE строки, которые меняются → Я всегда забываю об этом с помощью триггеров.

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

Ответ 5

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

Установите значение true в триггере, и триггер ничего не делает, если значение true в "INSERTED"

Ответ 6

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

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

От MSDN:

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

Ссылка: Избегать рекурсивных триггеров

Ответ 7

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

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