Триггеры сложны, и вам нужно подумать, когда вы их создаете. Триггер срабатывает один раз для каждого оператора UPDATE. Если этот оператор UPDATE обновляет несколько строк, триггер будет срабатывать только один раз. Функция UPDATE () возвращает true для столбца, когда этот столбец включен в инструкцию UPDATE. Эта функция помогает повысить эффективность триггеров, позволяя обойти логику SQL, когда этот столбец даже не включен в оператор обновления. Он не сообщает, изменилось ли значение для столбца в данной строке.
Вот пример таблицы ...
CREATE TABLE tblSample
(
SampleID INT PRIMARY KEY,
SampleName VARCHAR(10),
SampleNameLastChangedDateTime DATETIME,
Parent_SampleID INT
)
Если для этой таблицы использовался следующий SQL:
UPDATE tblSample SET SampleName = 'hello'
.. и срабатывал триггер AFTER INSERT, UPDATE, этот конкретный оператор SQL всегда будет оценивать функцию UPDATE следующим образом ...
IF UPDATE(SampleName) --aways evaluates to TRUE
IF UPDATE(SampleID) --aways evaluates to FALSE
IF UPDATE(Parent_SampleID) --aways evaluates to FALSE
Обратите внимание, что UPDATE (SampleName) всегда будет истинным для этого оператора SQL, независимо от того, какие значения SampleName были раньше. Он возвращает true, потому что инструкция UPDATE включает столбец SampleName в разделе SET этого предложения, а не на основе значений, которые были до или после. Функция UPDATE () не будет определять, изменились ли значения. Если вы хотите выполнять действия в зависимости от того, были ли изменены значения, вам нужно использовать SQL и сравнить вставленные и удаленные строки.
Вот подход к синхронизации последнего обновленного столбца:
--/*
IF OBJECT_ID('dbo.tgr_tblSample_InsertUpdate', 'TR') IS NOT NULL
DROP TRIGGER dbo.tgr_tblSample_InsertUpdate
GO
--*/
CREATE TRIGGER dbo.tgr_tblSample_InsertUpdate ON dbo.tblSample
AFTER INSERT, UPDATE
AS
BEGIN --Trigger
IF UPDATE(SampleName)
BEGIN
UPDATE tblSample SET
SampleNameLastChangedDateTime = CURRENT_TIMESTAMP
WHERE
SampleID IN (SELECT Inserted.SampleID
FROM Inserted LEFT JOIN Deleted ON Inserted.SampleID = Deleted.SampleID
WHERE COALESCE(Inserted.SampleName, '') <> COALESCE(Deleted.SampleName, ''))
END
END --Trigger
Логика определения того, была ли строка обновлена, приведена в предложении WHERE выше. Это реальная проверка, которую вам нужно сделать. Моя логика использует COALESCE для обработки значений NULL и INSERTS.
...
WHERE
SampleID IN (SELECT Inserted.SampleID
FROM Inserted LEFT JOIN Deleted ON Inserted.SampleID = Deleted.SampleID
WHERE COALESCE(Inserted.SampleName, '') <> COALESCE(Deleted.SampleName, ''))
Обратите внимание, что проверка IF UPDATE () используется для повышения эффективности триггера, когда столбец SampleName НЕ обновляется. Например, если оператор SQL обновил столбец Parent_SampleID, то проверка IF UPDATE (SampleName) поможет обойти более сложную логику в этом операторе IF, когда его не нужно запускать. Попробуйте использовать UPDATE (), когда это уместно, но не по неправильной причине.
Также следует понимать, что в зависимости от вашей архитектуры функция UPDATE может быть вам не нужна. Если ваша архитектура кода использует промежуточный уровень, который всегда обновляет все столбцы в строке таблицы значениями в бизнес-объекте при сохранении объекта, функция UPDATE () в триггере становится бесполезной. В этом случае ваш код, вероятно, всегда обновляет все столбцы с каждым оператором UPDATE, выданным из промежуточного уровня. В этом случае функция UPDATE (имя столбца) всегда будет иметь значение true при сохранении бизнес-объектов, поскольку все имена столбцов всегда включаются в операторы обновления. В этом случае было бы нецелесообразно использовать UPDATE () в триггере, а просто потребовалось бы дополнительное время в этом триггере в течение большей части времени.
Вот немного SQL, чтобы поиграть с вышеуказанным триггером:
INSERT INTO tblSample
(
SampleID,
SampleName
)
SELECT 1, 'One'
UNION SELECT 2, 'Two'
UNION SELECT 3, 'Three'
GO
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample
/*
SampleID SampleName SampleNameLastChangedDateTime
----------- ---------- -----------------------------
1 One 2010-10-27 14:52:42.567
2 Two 2010-10-27 14:52:42.567
3 Three 2010-10-27 14:52:42.567
*/
GO
INSERT INTO tblSample
(
SampleID,
SampleName
)
SELECT 4, 'Foo'
UNION SELECT 5, 'Five'
GO
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample
/*
SampleID SampleName SampleNameLastChangedDateTime
----------- ---------- -----------------------------
1 One 2010-10-27 14:52:42.567
2 Two 2010-10-27 14:52:42.567
3 Three 2010-10-27 14:52:42.567
4 Foo 2010-10-27 14:52:42.587
5 Five 2010-10-27 14:52:42.587
*/
GO
UPDATE tblSample SET SampleName = 'Foo'
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample
/*
SampleID SampleName SampleNameLastChangedDateTime
----------- ---------- -----------------------------
1 Foo 2010-10-27 14:52:42.657
2 Foo 2010-10-27 14:52:42.657
3 Foo 2010-10-27 14:52:42.657
4 Foo 2010-10-27 14:52:42.587
5 Foo 2010-10-27 14:52:42.657
*/
GO
UPDATE tblSample SET SampleName = 'Not Prime' WHERE SampleID IN (1,4)
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample
/*
SampleID SampleName SampleNameLastChangedDateTime
----------- ---------- -----------------------------
1 Not Prime 2010-10-27 14:52:42.680
2 Foo 2010-10-27 14:52:42.657
3 Foo 2010-10-27 14:52:42.657
4 Not Prime 2010-10-27 14:52:42.680
5 Foo 2010-10-27 14:52:42.657
*/
--Clean up...
DROP TRIGGER dbo.tgr_tblSample_InsertUpdate
DROP TABLE tblSample
Пользователь GBN предложил следующее:
IF EXISTS (
SELECT
*
FROM
INSERTED I
JOIN
DELETED D ON I.key = D.key
WHERE
D.valuecol <> I.valuecol --watch for NULLs!
)
blah
Предложение GBN об использовании предложения IF (EXISTS (...) и помещении логики в этот оператор IF, если существующие строки были изменены, может сработать. Этот подход будет срабатывать для ВСЕХ строк, включенных в триггер, даже если только некоторые из строки были фактически изменены (что может подходить для вашего решения, но также может не подходить, если вы хотите что-то сделать только со строками, в которых изменились значения.) Если вам нужно что-то сделать со строками, в которых произошло реальное изменение, вы нужна другая логика в вашем SQL, которую он предоставил.
В моих приведенных выше примерах, когда выдается инструкция UPDATE tblSample SET SampleName = 'Foo', а четвертая строка уже является 'foo', использование подхода GBN для обновления столбца «последнее измененное время-дата» также обновляет четвертую строку, что не подходит в этом случае.