CHECKSUM()Gerçek değerlerin değiştirilip değiştirilmediğini karşılaştırmak için oldukça basit bir yöntem olarak kullanabilirsiniz . CHECKSUM()sayısı ve türü belirsiz olan bir geçirilen değerler listesinde sağlama toplamı oluşturur. Dikkat edin, bunun gibi sağlama toplamlarını karşılaştırmanın küçük bir şansı vardır. Bununla başa çıkamıyorsanız, HASHBYTESbunun yerine 1'i kullanabilirsiniz .
Aşağıdaki örnek AFTER UPDATE, TriggerTestyalnızca Data1 veya Data2 sütunlarındaki değerlerden biri değiştiğinde tabloda yapılan değişikliklerin geçmişini korumak için bir tetikleyici kullanır . Data3Değişiklik olursa herhangi bir işlem yapılmaz.
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

COLUMNS_UPDATED () işlevini kullanmakta ısrar ediyorsanız , söz konusu sütunların sıra değerini sabit kodlamamalısınız, çünkü tablo tanımı değişebilir, bu da sabit kodlanmış değerleri geçersiz kılabilir. Sistem tablolarını kullanarak çalışma zamanında değerin ne olması gerektiğini hesaplayabilirsiniz. COLUMNS_UPDATED()Sütun, ifadeden etkilenen HERHANGİ bir satırda değiştirilirse , işlevin verilen sütun biti için true değerini döndürdüğünü unutmayın UPDATE TABLE.
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;

Bu demo, tarih tablosuna belki de eklenmemesi gereken satırları ekler. Satırların Data1sütunları bazı satırlar Data3için güncellendi ve sütun bazı satırlar için güncellendi. Bu tek bir ifade olduğundan, tüm satırlar tetikleyiciden tek bir geçişle işlenir. Karşılaştırmanın bir Data1parçası olan bazı satırlar güncellendiğinden, COLUMNS_UPDATED()tetikleyici tarafından görülen tüm satırlar TriggerHistorytabloya eklenir . Senaryonuz için bu "yanlış" ise, imleç kullanarak her satırı ayrı ayrı işlemeniz gerekebilir.
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;
TriggerResultTablo şimdi onlar (o tablodaki iki sütun) kesinlikle hiçbir değişiklik göstermesi nedeniyle aittir değil mi benziyor bazı potansiyel yanıltıcı satır var. Aşağıdaki görüntüdeki 2. satır kümesinde, TriggerTestID 7 değiştirilmiş gibi görünen tek satırdır. Diğer satırlarda yalnızca Data3sütun güncellendi; ancak toplu işteki bir satır Data1güncellendiğinden, tüm satırlar TriggerResulttabloya eklenir .

Alternatif olarak, @AaronBertrand ve @srutzky'nin de belirttiği gibi, insertedve deletedsanal tablolardaki gerçek verilerin bir karşılaştırmasını yapabilirsiniz . Her iki tablonun yapısı aynı olduğundan, EXCEPTilgilendiğiniz kesin sütunların değiştiği satırları yakalamak için tetikleyicide bir cümle kullanabilirsiniz :
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 - bkz /programming/297960/hash-collision-what-are-the-chances HASHBYTES hesaplama da çarpışmaları neden olabileceğini yok denecek kadar az şans discsussion için. Preshing de bu sorunun iyi bir analizine sahiptir.
SETlistede geçip geçmediğini veya değerlerin gerçekten değişip değişmediğini bilmek ister misiniz ? HemUPDATEveCOLUMNS_UPDATED()size sadece eski söyle. Eğer değerler aslında değişti olmadığını bilmek istiyorsanız, uygun bir karşılaştırma yapmak gerekirinsertedvedeleted.