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, HASHBYTES
bunun yerine 1'i kullanabilirsiniz .
Aşağıdaki örnek AFTER UPDATE
, TriggerTest
yalnı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 . Data3
Değ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 Data1
sütunları bazı satırlar Data3
iç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 Data1
parçası olan bazı satırlar güncellendiğinden, COLUMNS_UPDATED()
tetikleyici tarafından görülen tüm satırlar TriggerHistory
tabloya 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;
TriggerResult
Tablo ş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 Data3
sütun güncellendi; ancak toplu işteki bir satır Data1
güncellendiğinden, tüm satırlar TriggerResult
tabloya eklenir .
Alternatif olarak, @AaronBertrand ve @srutzky'nin de belirttiği gibi, inserted
ve deleted
sanal tablolardaki gerçek verilerin bir karşılaştırmasını yapabilirsiniz . Her iki tablonun yapısı aynı olduğundan, EXCEPT
ilgilendiğ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.
SET
listede geçip geçmediğini veya değerlerin gerçekten değişip değişmediğini bilmek ister misiniz ? HemUPDATE
veCOLUMNS_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 gerekirinserted
vedeleted
.