Предотвратить взаимно рекурсивное выполнение триггеров? - PullRequest
17 голосов
/ 10 января 2009

Предположим, у вас есть таблицы 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 в обоих триггерах.

1 Ответ

19 голосов
/ 10 января 2009

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

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

...