Предотвратить взаимно рекурсивное выполнение триггеров?

Предположим, у вас есть таблицы Presentations и Events. Когда презентация сохранена и содержит базовую информацию о событии, такую ​​как место и дата, событие будет создано автоматически с помощью триггера. (Боюсь, по техническим причинам невозможно просто хранить данные в одном месте и использовать представление.) Кроме того, при изменении этой информации позже в презентации триггер также скопирует обновления в событие, вот так:

CREATE TRIGGER update_presentations
ON Presentations
AFTER UPDATE
AS
BEGIN
    UPDATE Events
    SET Events.Date = Presentations.Date,
        Events.Location = Presentations.Location
    FROM Presentations INNER JOIN Events ON Presentations.EventID = Events.ID
    WHERE Presentations.ID IN (SELECT ID FROM inserted)
END

Теперь заказчик хочет, чтобы, если пользователь когда-либо изменит информацию в событии, она также вернулась в презентацию. По понятным причинам я не могу сделать обратное:

CREATE TRIGGER update_events
ON Events
AFTER UPDATE
AS
BEGIN
    UPDATE Presentations
    SET Presentations.Date = Events.Date,
        Presentations.Location = Events.Location
    FROM Events INNER JOIN Presentations ON Events.PresentationID = Presentations.ID
    WHERE Events.ID IN (SELECT ID FROM inserted)
END

В конце концов, это заставит каждый триггер срабатывать один за другим. Что я мог бы сделать, так это добавить столбец last_edit_by в обе таблицы, содержащий идентификатор пользователя. Если бы триггер заполнил его специальным недопустимым идентификатором (скажем, сделав все идентификаторы пользователей реальных людей положительными, а идентификаторы пользователей скриптов отрицательными), я мог бы использовать это как условие выхода:

    AND last_edit_by >= 0

Это может сработать, но я хотел бы указать SQL-серверу, что в рамках транзакции триггер должен срабатывать только один раз. Есть ли способ проверить это? Или, возможно, чтобы проверить, что таблица уже была затронута триггером?


Ответ благодаря Стиву Роббинсу:

Просто оберните потенциально вложенные операторы UPDATE в проверку условия IF для trigger_nestlevel(). Например:

CREATE TRIGGER update_presentations
ON Presentations
AFTER UPDATE
AS
BEGIN
    IF trigger_nestlevel() < 2
        UPDATE Events
        SET Events.Date = Presentations.Date,
            Events.Location = Presentations.Location
        FROM Presentations INNER JOIN Events ON Presentations.EventID = Events.ID
        WHERE Presentations.ID IN (SELECT ID FROM inserted)
END

Обратите внимание, что trigger_nestlevel() выглядит отсчитываемым от 1, а не от 0. Если вы хотите, чтобы каждый из двух триггеров выполнялся один раз, но не чаще, просто проверьте наличие trigger_nestlevel() < 3 в обоих триггерах.


person Sören Kuklau    schedule 10.01.2009    source источник


Ответы (1)


Я не уверен, что нужно делать это для каждой транзакции, но нужны ли вам вложенные триггеры для других частей? Если вы отключите их на сервере, триггер не будет срабатывать из другого триггера, обновляющего таблицу.

РЕДАКТИРОВАТЬ (ответ из комментариев): Вам нужно будет изменить триггер A, чтобы использовать TRIGGER_NESTLEVEL

person Steven Robbins    schedule 10.01.2009
comment
Вложенные триггеры фактически отключены в этой базе данных. Моя проблема заключается в том, что триггер A запускает B, запуская A, а не A напрямую запускает A. Настройка этому не препятствует. - person Sören Kuklau; 10.01.2009
comment
Это рекурсивные триггеры, я сейчас не на машине, но я уверен, что есть и вложенные триггеры, которые вы можете настроить с помощью sp_config. Однако прошло несколько лет с тех пор, как я столкнулся с этой проблемой. - person Steven Robbins; 10.01.2009
comment
Ты прав; вложенные триггеры - еще один вариант. Но то, как я прочитал его документацию, предотвратило бы срабатывание B после A, тогда как я хочу предотвратить срабатывание A во второй раз. - person Sören Kuklau; 10.01.2009
comment
Ой, извините, тогда я неправильно прочитал ваш пост. Вам нужно будет изменить триггер A, чтобы использовать TRIGGER_NESTLEVEL (msdn.microsoft.com/en -us/library/ms182737.aspx) и не запускаться, когда он вложен. - person Steven Robbins; 10.01.2009
comment
Похоже, это именно то, что я искал! :) - person Sören Kuklau; 10.01.2009
comment
Прохладно. Извините, ответ был немного по домам, неправильно понял, что вы хотели. - person Steven Robbins; 10.01.2009