Bạn có thể sử dụng CHECKSUM()
như một phương pháp khá đơn giản để so sánh các giá trị thực tế để xem chúng có bị thay đổi hay không. CHECKSUM()
sẽ tạo tổng kiểm tra qua danh sách các giá trị được truyền vào, trong đó số lượng và loại không xác định. Coi chừng, có một cơ hội nhỏ so sánh tổng kiểm như thế này sẽ dẫn đến kết quả âm tính giả. Nếu bạn không thể đối phó với điều đó, bạn có thể sử dụng HASHBYTES
thay vì 1 .
Ví dụ dưới đây sử dụng trình AFTER UPDATE
kích hoạt để giữ lại lịch sử sửa đổi được thực hiện cho TriggerTest
bảng chỉ khi một trong hai giá trị trong Data1
hoặc Data2
cột thay đổi. Nếu Data3
thay đổi, không có hành động được thực hiện.
USE tempdb;
IF COALESCE(OBJECT_ID('dbo.TriggerTest'), 0) <> 0
BEGIN
DROP TABLE dbo.TriggerTest;
END
CREATE TABLE dbo.TriggerTest
(
TriggerTestID INT NOT NULL
CONSTRAINT PK_TriggerTest
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, Data1 VARCHAR(10) NULL
, Data2 VARCHAR(10) NOT NULL
, Data3 DATETIME NOT NULL
);
IF COALESCE(OBJECT_ID('dbo.TriggerResult'), 0) <> 0
BEGIN
DROP TABLE dbo.TriggerResult;
END
CREATE TABLE dbo.TriggerResult
(
TriggerTestID INT NOT NULL
, Data1OldVal VARCHAR(10) NULL
, Data1NewVal VARCHAR(10) NULL
, Data2OldVal VARCHAR(10) NULL
, Data2NewVal VARCHAR(10) NULL
);
GO
IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0
BEGIN
DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS
BEGIN
INSERT INTO TriggerResult
(
TriggerTestID
, Data1OldVal
, Data1NewVal
, Data2OldVal
, Data2NewVal
)
SELECT d.TriggerTestID
, d.Data1
, i.Data1
, d.Data2
, i.Data2
FROM inserted i
LEFT JOIN deleted d ON i.TriggerTestID = d.TriggerTestID
WHERE CHECKSUM(i.Data1, i.Data2) <> CHECKSUM(d.Data1, d.Data2);
END
GO
INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
VALUES ('blah', 'foo', GETDATE());
UPDATE dbo.TriggerTest
SET Data1 = 'blah', Data2 = 'fee'
WHERE TriggerTestID = 1;
SELECT *
FROM dbo.TriggerTest;
SELECT *
FROM dbo.TriggerResult
Nếu bạn khăng khăng sử dụng hàm COLUMNS_UPDATED () , bạn không nên mã hóa giá trị thứ tự của các cột trong câu hỏi, vì định nghĩa bảng có thể thay đổi, có thể làm mất hiệu lực (các) giá trị được mã hóa cứng. Bạn có thể tính toán giá trị nên có trong thời gian chạy bằng cách sử dụng các bảng hệ thống. Xin lưu ý rằng COLUMNS_UPDATED()
hàm trả về true cho bit cột đã cho nếu cột bị sửa đổi trong bất kỳ hàng nào bị ảnh hưởng bởi UPDATE TABLE
câu lệnh.
USE tempdb;
IF COALESCE(OBJECT_ID('dbo.TriggerTest'), 0) <> 0
BEGIN
DROP TABLE dbo.TriggerTest;
END
CREATE TABLE dbo.TriggerTest
(
TriggerTestID INT NOT NULL
CONSTRAINT PK_TriggerTest
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, Data1 VARCHAR(10) NULL
, Data2 VARCHAR(10) NOT NULL
, Data3 DATETIME NOT NULL
);
IF COALESCE(OBJECT_ID('dbo.TriggerResult'), 0) <> 0
BEGIN
DROP TABLE dbo.TriggerResult;
END
CREATE TABLE dbo.TriggerResult
(
TriggerTestID INT NOT NULL
, Data1OldVal VARCHAR(10) NULL
, Data1NewVal VARCHAR(10) NULL
, Data2OldVal VARCHAR(10) NULL
, Data2NewVal VARCHAR(10) NULL
);
GO
IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0
BEGIN
DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS
BEGIN
DECLARE @ColumnOrdinalTotal INT = 0;
SELECT @ColumnOrdinalTotal = @ColumnOrdinalTotal
+ POWER (
2
, COLUMNPROPERTY(t.object_id,c.name,'ColumnID') - 1
)
FROM sys.schemas s
INNER JOIN sys.tables t ON s.schema_id = t.schema_id
INNER JOIN sys.columns c ON t.object_id = c.object_id
WHERE s.name = 'dbo'
AND t.name = 'TriggerTest'
AND c.name IN (
'Data1'
, 'Data2'
);
IF (COLUMNS_UPDATED() & @ColumnOrdinalTotal) > 0
BEGIN
INSERT INTO TriggerResult
(
TriggerTestID
, Data1OldVal
, Data1NewVal
, Data2OldVal
, Data2NewVal
)
SELECT d.TriggerTestID
, d.Data1
, i.Data1
, d.Data2
, i.Data2
FROM inserted i
LEFT JOIN deleted d ON i.TriggerTestID = d.TriggerTestID;
END
END
GO
--this won't result in rows being inserted into the history table
INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
VALUES ('blah', 'foo', GETDATE());
SELECT *
FROM dbo.TriggerResult;
--this will insert rows into the history table
UPDATE dbo.TriggerTest
SET Data1 = 'blah', Data2 = 'fee'
WHERE TriggerTestID = 1;
SELECT *
FROM dbo.TriggerTest;
SELECT *
FROM dbo.TriggerResult;
--this WON'T insert rows into the history table
UPDATE dbo.TriggerTest
SET Data3 = GETDATE()
WHERE TriggerTestID = 1;
SELECT *
FROM dbo.TriggerTest;
SELECT *
FROM dbo.TriggerResult
--this will insert rows into the history table, even though only
--one of the columns was updated
UPDATE dbo.TriggerTest
SET Data1 = 'blum'
WHERE TriggerTestID = 1;
SELECT *
FROM dbo.TriggerTest;
SELECT *
FROM dbo.TriggerResult;
Bản demo này chèn các hàng vào bảng lịch sử có lẽ không nên chèn. Các hàng đã được Data1
cập nhật cột cho một số hàng và đã Data3
cập nhật cột cho một số hàng. Vì đây là một câu lệnh đơn, tất cả các hàng được xử lý bằng một lần chuyển qua trình kích hoạt. Vì một số hàng đã Data1
được cập nhật, là một phần của COLUMNS_UPDATED()
so sánh, tất cả các hàng được xem bởi trình kích hoạt sẽ được chèn vào TriggerHistory
bảng. Nếu điều này là "không chính xác" cho kịch bản của bạn, bạn có thể cần xử lý từng hàng riêng biệt bằng cách sử dụng một con trỏ.
INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
SELECT TOP(10) LEFT(o.name, 10)
, LEFT(o1.name, 10)
, GETDATE()
FROM sys.objects o
, sys.objects o1;
UPDATE dbo.TriggerTest
SET Data1 = CASE WHEN TriggerTestID % 6 = 1 THEN Data2 ELSE Data1 END
, Data3 = CASE WHEN TriggerTestID % 6 = 2 THEN GETDATE() ELSE Data3 END;
SELECT *
FROM dbo.TriggerTest;
SELECT *
FROM dbo.TriggerResult;
Các TriggerResult
bảng hiện nay có một số hàng có khả năng gây hiểu lầm mà hình như họ không thuộc vì họ thể hiện hoàn toàn không có sự thay đổi (với hai cột trong bảng đó). Trong tập hợp hàng thứ 2 trong hình ảnh bên dưới, TriggerTestID 7 là hàng duy nhất trông giống như được sửa đổi. Các hàng khác chỉ có Data3
cột được cập nhật; tuy nhiên vì một hàng trong lô đã Data1
được cập nhật, tất cả các hàng được chèn vào TriggerResult
bảng.
Cách khác, như @AaronBertrand và @srutzky đã chỉ ra, bạn có thể thực hiện so sánh dữ liệu thực tế trong các bảng inserted
và deleted
ảo. Vì cấu trúc của cả hai bảng giống hệt nhau, bạn có thể sử dụng một EXCEPT
mệnh đề trong trình kích hoạt để chụp các hàng trong đó các cột chính xác mà bạn quan tâm đã thay đổi:
IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0
BEGIN
DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS
BEGIN
;WITH src AS
(
SELECT d.TriggerTestID
, d.Data1
, d.Data2
FROM deleted d
EXCEPT
SELECT i.TriggerTestID
, i.Data1
, i.Data2
FROM inserted i
)
INSERT INTO dbo.TriggerResult
(
TriggerTestID,
Data1OldVal,
Data1NewVal,
Data2OldVal,
Data2NewVal
)
SELECT i.TriggerTestID
, d.Data1
, i.Data1
, d.Data2
, i.Data2
FROM inserted i
INNER JOIN deleted d ON i.TriggerTestID = d.TriggerTestID
END
GO
1 - xem /programming/297960/hash-collision-what-are-the-chances cho một sự bất đồng về cơ hội nhỏ nhoi mà tính toán HASHBYTES cũng có thể dẫn đến va chạm. Preshing có một phân tích tốt về vấn đề này là tốt.
SET
danh sách, hoặc nếu các giá trị thực sự thay đổi? Cả haiUPDATE
vàCOLUMNS_UPDATED()
chỉ cho bạn biết trước đây. Nếu bạn muốn biết nếu các giá trị thực sự thay đổi, bạn sẽ cần phải so sánh đúnginserted
vàdeleted
.