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

SQL Server - после запуска триггера - обновить другой столбец в той же таблице

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

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 [email protected]

GO

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

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

Пожалуйста, не задирайте в верхнем случае. Сумасшедшая ситуация.

4b9b3361

Ответ 1

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

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

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: он работает для вставки/обновления мультироста (ваш пример не подходит).

Ответ 2

Другой вариант заключается в том, чтобы заключить оператор обновления в оператор 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;

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

НТН

Dave

Ответ 5

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

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

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

Ответ 6

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

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

IF UPDATE(part_description_upper)
  return

Ответ 7

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 как null, оно обновит столбец как null для каждого оператора insert.