SQL Server - после триггера вставки - обновить другой столбец в той же таблице - PullRequest
24 голосов
/ 17 марта 2011

У меня есть триггер базы данных:

CREATE TRIGGER setDescToUpper
ON part_numbers
 AFTER INSERT,UPDATE
AS
DECLARE @PnumPkid int, @PDesc nvarchar(128)

SET @PnumPkid = (SELECT pnum_pkid FROM inserted)
SET @PDesc = (SELECT UPPER(part_description) FROM inserted)

UPDATE part_numbers set part_description_upper = @PDesc WHERE pnum_pkid=@PnumPkid

GO

Это плохая идея? То есть обновить столбец в той же таблице.Я хочу, чтобы он запускался как для вставки, так и для обновления.

Это работает, я просто боюсь циклической ситуации.Обновление внутри триггера запускает триггер, и снова и снова. Это произойдет?

Пожалуйста, не придирайтесь к заглавным буквам.Безумная ситуация.

Ответы [ 7 ]

39 голосов
/ 17 марта 2011

Это зависит от уровня рекурсии для триггеров, установленных в данный момент в БД.

Если вы сделаете это:

SP_CONFIGURE 'nested_triggers',0
GO
RECONFIGURE
GO

Или это:

ALTER DATABASE db_name
SET RECURSIVE_TRIGGERS OFF

Этот вышеупомянутый триггер больше не будет вызываться, и вы будете в безопасности (если только вы не попадете в какой-то тупик; это возможно, но, возможно, я ошибаюсь).

Тем не менее, я не думаю, что это хорошая идея. Лучшим вариантом было бы использовать INSTEAD OF триггер . Таким образом, вы бы не выполняли первое (ручное) обновление через БД. Будет выполнен только тот, который определен внутри триггера.

Триггер INSTEAD OF INSERT будет выглядеть так:

CREATE TRIGGER setDescToUpper ON part_numbers
INSTEAD OF INSERT
AS
BEGIN
    INSERT INTO part_numbers (
        colA,
        colB,
        part_description
    ) SELECT
        colA,
        colB,
        UPPER(part_description)
    ) FROM
        INSERTED
END
GO

Это автоматически "заменит" исходный оператор INSERT на этот, с явным вызовом UPPER, примененным к полю part_description.

Триггер INSTEAD OF UPDATE будет похож (и я не советую вам создавать один триггер, держите их отдельно).

Кроме того, это адрес @Martin comment: он работает для многорядных вставок / обновлений (ваш пример - нет).

15 голосов
/ 17 декабря 2011

Другой вариант - заключить оператор обновления в оператор IF и вызвать TRIGGER_NESTLEVEL () , чтобы ограничить запуск обновления во второй раз.

CREATE TRIGGER Table_A_Update ON Table_A AFTER UPDATE 
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
    UPDATE a
    SET Date_Column = GETDATE()
    FROM Table_A a
    JOIN inserted i ON a.ID = i.ID
END

Когда триггер изначальнозапускает TRIGGER_NESTLEVEL, равное 1, поэтому оператор обновления будет выполнен.Этот оператор обновления, в свою очередь, запустит тот же триггер, за исключением того, что TRIGGER_NESTLEVEL установлен на 2, и оператор обновления не будет выполнен.

Вы также можете сначала проверить TRIGGER_NESTLEVEL, и если его значение больше 1, вызвать RETURNвыйти из триггера.

IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
6 голосов
/ 17 марта 2011

Вместо этого используйте вычисляемый столбец.Почти всегда лучше использовать вычисляемый столбец, чем триггер.

См. Пример ниже вычисляемого столбца с использованием функции UPPER:

create table #temp (test varchar (10), test2 AS upper(test))
insert #temp (test)
values ('test')
select * from #temp

И не для того, чтобы звучать как поврежденная запись или что-то еще, но это критически важно.Никогда не пишите триггер, который не будет работать корректно при вставке / обновлении / удалении нескольких записей.Это крайне плохая практика, так как рано или поздно произойдет одно из этих событий, и ваш триггер вызовет проблемы с целостностью данных, поскольку он точно не выйдет из строя, он только запустит процесс на одной из записей.Это может занять много времени, пока кто-то не обнаружит беспорядок, и с помощью него зачастую невозможно правильно исправить данные.

1 голос
/ 04 февраля 2015

Может быть безопаснее выйти из триггера, когда нечего делать.Проверка уровня вложенности или изменение базы данных путем отключения RECURSIVE могут вызывать проблемы.

Ms sql предоставляет простой способ в триггере посмотреть, обновлены ли определенные столбцы.Используйте метод UPDATE (), чтобы узнать, были ли обновлены определенные столбцы, например, UPDATE (part_description_upper).

IF UPDATE(part_description_upper)
  return
1 голос
/ 17 марта 2011

Да, он будет рекурсивно вызывать ваш триггер, если вы не отключите настройку рекурсивных триггеров:

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

MSDN имеет хорошее объяснение поведения в http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx под заголовком Рекурсивные триггеры.

1 голос
/ 17 марта 2011

Да ... наличие дополнительного шага для обновления таблицы, в которой вы можете установить значение в начальной вставке, вероятно, является дополнительным процессом, которого можно избежать. У вас есть доступ к исходному оператору вставки, где вы можете просто вставить part_description в столбец part_description_upper, используя значение UPPER (part_description)?

Подумав, у вас, вероятно, нет доступа, поскольку вы, вероятно, сделали бы это, поэтому должны также предоставить некоторые варианты ...

1) Зависит от потребности в этом столбце part_description_upper, если только для «просмотра», то можно просто использовать возвращенное значение part_description и «ToUpper ()» (в зависимости от языка программирования).

2) Если вы хотите избежать обработки в реальном времени, просто создайте задание sql для просмотра ваших значений один раз в день в периоды низкого трафика и обновите в этом столбце значение UPPER part_description для всех, которые в данный момент не установлены.

3) иди с триггером (и следи за рекурсией, как уже упоминали другие) ...

НТН

Dave

0 голосов
/ 04 сентября 2013
create or replace 
TRIGGER triggername BEFORE INSERT  ON 
table FOR EACH ROW 
BEGIN
/*
Write any select condition if you want to get the data from other tables
*/
:NEW.COLUMNA:= UPPER(COLUMNA); 
--:NEW.COUMNa:= NULL;
END; 

Приведенный выше триггер обновит значение столбца перед вставкой. Например, если мы дадим значение COLUMNA как ноль, он будет обновлять столбец как ноль для каждого оператора вставки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...