觸發器很棘手,當您創建觸發器時需要大量思考。每個UPDATE語句觸發一次觸發器。如果該UPDATE語句更新多行,則該觸發器將只觸發一次。當該列包含在UPDATE語句中時,UPDATE()函數爲列返回true。該功能有助於提高觸發器的效率,即使更新語句中未包含該列,您也可以避開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
請注意,對於此SQL語句,UPDATE(SampleName)始終爲true,無論以前的SampleName值如何。它返回true,因爲UPDATE語句在該子句的SET部分中包含了SampleName列,而不是基於值之前或之後的值。 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(columnname)函數總是計算爲真,因爲所有列名都始終包含在更新語句中。在這種情況下,在觸發器中使用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
使用IF(中GBN的建議EXISTS(...條款,並把IF語句中的邏輯是否存在被更改的行可以工作,即使只有一些行實際發生了更改(這可能適用於您的解決方案,但也可能不是如果你只想做某件事,那就適當)。如果您需要對發生實際更改的行執行某些操作,則需要您提供的SQL中的不同邏輯。
在我上面的示例中,當發佈UPDATE tblSample SET SampleName ='Foo'語句並且第四行已經是'foo'時,使用GBN的方法更新「上次更改的日期時間」列也會更新第四行在這種情況下這不合適。
謝謝。 SQL服務器提供的其他任何可用選項來查找更改?或者我必須遍歷每一行才能找出變化? – 2009-08-08 14:49:57
只需加入密鑰上的2個表,不需要loosp需要 – gbn 2009-08-08 14:50:33
這就是'刪除'和'**插入**'虛擬表,對吧?我從來沒有聽說過「更新」虛擬表格 – 2009-08-08 14:51:28