Anda dapat menggunakan CHECKSUM()
metodologi yang cukup sederhana untuk membandingkan nilai aktual untuk melihat apakah mereka diubah. CHECKSUM()
akan menghasilkan checksum di seluruh daftar nilai yang diteruskan, di mana jumlah dan jenisnya tidak ditentukan. Hati-hati, ada kemungkinan kecil membandingkan checksum seperti ini akan menghasilkan negatif palsu. Jika Anda tidak dapat menghadapi itu, Anda dapat menggunakan HASHBYTES
sebagai gantinya 1 .
Contoh di bawah ini menggunakan AFTER UPDATE
pemicu untuk mempertahankan riwayat modifikasi yang dibuat ke TriggerTest
tabel hanya jika salah satu nilai dalam kolom Data1
atau Data2
berubah. Jika Data3
berubah, tidak ada tindakan yang diambil.
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
Jika Anda ngotot menggunakan fungsi COLUMNS_UPDATED () , Anda tidak boleh meng -hard-code nilai ordinal kolom yang bersangkutan, karena definisi tabel mungkin berubah, yang bisa membuat nilai-nilai hard-code tidak valid. Anda bisa menghitung berapa nilainya saat runtime menggunakan tabel sistem. Ketahuilah bahwa COLUMNS_UPDATED()
fungsi mengembalikan true untuk bit kolom yang diberikan jika kolom diubah dalam baris APA PUN yang dipengaruhi oleh UPDATE TABLE
pernyataan.
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;
Demo ini menyisipkan baris ke tabel sejarah yang mungkin tidak boleh dimasukkan. Baris telah Data1
diperbarui kolomnya untuk beberapa baris, dan Data3
kolom diperbarui untuk beberapa baris. Karena ini adalah pernyataan tunggal, semua baris diproses oleh satu melewati pemicu. Karena beberapa baris telah Data1
diperbarui, yang merupakan bagian dari COLUMNS_UPDATED()
perbandingan, semua baris yang dilihat oleh pelatuk dimasukkan ke dalam TriggerHistory
tabel. Jika ini "salah" untuk skenario Anda, Anda mungkin perlu menangani setiap baris secara terpisah, menggunakan kursor.
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;
The TriggerResult
meja sekarang memiliki beberapa baris berpotensi menyesatkan yang terlihat seperti mereka tidak milik karena mereka menunjukkan benar-benar tidak ada perubahan (ke dua kolom dalam tabel itu). Dalam rangkaian baris ke-2 pada gambar di bawah ini, TriggerTestID 7 adalah satu-satunya yang sepertinya dimodifikasi. Baris lain hanya memiliki Data3
kolom diperbarui; namun karena satu baris dalam kumpulan telah Data1
diperbarui, semua baris dimasukkan dalam TriggerResult
tabel.
Sebagai alternatif, seperti yang ditunjukkan oleh @AaronBertrand dan @srutzky, Anda dapat melakukan perbandingan data aktual di inserted
dan deleted
tabel virtual. Karena struktur kedua tabel identik, Anda bisa menggunakan EXCEPT
klausa di pemicu untuk menangkap baris di mana kolom yang tepat Anda tertarik telah berubah:
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 - lihat /programming/297960/hash-collision-what-are-the-chances untuk diskusi tentang peluang kecil yang semakin menghilang sehingga perhitungan HASHBYTES juga dapat mengakibatkan tabrakan. Preshing juga memiliki analisis yang layak untuk masalah ini.
SET
daftar, atau apakah nilainya benar-benar berubah? KeduanyaUPDATE
danCOLUMNS_UPDATED()
hanya memberitahu Anda yang pertama. Jika Anda ingin tahu apakah nilainya benar-benar berubah, Anda harus melakukan perbandinganinserted
dandeleted
.