คุณสามารถใช้CHECKSUM()
เป็นวิธีการที่ค่อนข้างง่ายสำหรับการเปรียบเทียบค่าจริงเพื่อดูว่ามีการเปลี่ยนแปลงหรือไม่ CHECKSUM()
จะสร้างการตรวจสอบข้ามรายการของค่าที่ส่งผ่านซึ่งจำนวนและประเภทจะไม่แน่นอน ระวังมีโอกาสเล็กน้อยที่จะเปรียบเทียบเช็คซัมแบบนี้จะส่งผลให้เกิดการลบในทางที่ผิด ถ้าคุณไม่สามารถจัดการกับที่คุณสามารถใช้HASHBYTES
แทน1
ตัวอย่างด้านล่างใช้AFTER UPDATE
ทริกเกอร์เพื่อเก็บประวัติการแก้ไขที่ทำไว้กับTriggerTest
ตารางเฉพาะเมื่อค่าใดค่าหนึ่งในData1
หรือ Data2
คอลัมน์เปลี่ยนไป หากมีData3
การเปลี่ยนแปลงจะไม่มีการดำเนินการใด ๆ
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 ()คุณไม่ควรฮาร์ดโค้ดค่าเลขลำดับของคอลัมน์ที่เป็นปัญหาเนื่องจากคำจำกัดความของตารางอาจเปลี่ยนแปลงซึ่งอาจทำให้ค่ารหัสตายตัวใช้ไม่ได้ คุณสามารถคำนวณค่าที่ควรเป็นตอนรันไทม์โดยใช้ตารางระบบ โปรดทราบว่าCOLUMNS_UPDATED()
ฟังก์ชันจะส่งกลับค่าจริงสำหรับบิตของคอลัมน์ที่กำหนดหากคอลัมน์นั้นได้รับการแก้ไขในแถวใดก็ได้ที่ได้รับผลกระทบจาก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;
การสาธิตนี้แทรกแถวลงในตารางประวัติที่ไม่ควรแทรก แถวมีData1
คอลัมน์ที่อัปเดตสำหรับบางแถวและมีData3
คอลัมน์ที่อัปเดตสำหรับบางแถว เนื่องจากนี่เป็นคำสั่งเดียวแถวทั้งหมดจึงถูกประมวลผลด้วยการส่งผ่านครั้งเดียวผ่านทริกเกอร์ เนื่องจากบางแถวมีData1
การอัพเดตซึ่งเป็นส่วนหนึ่งของการCOLUMNS_UPDATED()
เปรียบเทียบแถวทั้งหมดที่เห็นโดยทริกเกอร์จะถูกแทรกลงในTriggerHistory
ตาราง หากนี่เป็น "ไม่ถูกต้อง" สำหรับสถานการณ์ของคุณคุณอาจต้องจัดการแต่ละแถวแยกกันโดยใช้เคอร์เซอร์
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
ตารางตอนนี้มีแถวที่อาจทำให้เข้าใจผิดบางอย่างที่ดูเหมือนว่าพวกเขาไม่ได้เป็นเพราะพวกเขาแสดงให้เห็นอย่างไม่มีการเปลี่ยนแปลง (ทั้งสองคอลัมน์ในตารางนั้น) ในแถวที่ 2 ในรูปภาพด้านล่าง TriggerTestID 7 เป็นเพียงแถวเดียวที่ดูเหมือนจะถูกแก้ไข แถวอื่น ๆ มีการData3
อัปเดตคอลัมน์เท่านั้น อย่างไรก็ตามตั้งแต่หนึ่งแถวในแบตช์ได้Data1
รับการปรับปรุงแถวทั้งหมดจะถูกแทรกในTriggerResult
ตาราง
อีกวิธีหนึ่งคือ @AaronBertrand และ @srutzky ชี้ให้เห็นคุณสามารถทำการเปรียบเทียบข้อมูลจริงในตารางเสมือนinserted
และ deleted
เนื่องจากโครงสร้างของทั้งสองตารางเหมือนกันคุณสามารถใช้EXCEPT
ส่วนในทริกเกอร์เพื่อจับแถวที่มีการเปลี่ยนแปลงคอลัมน์ที่แม่นยำที่คุณสนใจ:
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 - ดูที่/programming/297960/hash-collision-what-are-the-chancesสำหรับการแยกแยะโอกาสเล็ก ๆ ที่หายไปซึ่งการคำนวณ HASHBYTES อาจส่งผลให้เกิดการชนกัน Preshingมีการวิเคราะห์ปัญหานี้เช่นกัน
SET
รายการหรือไม่หรือถ้าค่านั้นเปลี่ยนไปจริง ๆ ? ทั้งคู่UPDATE
และCOLUMNS_UPDATED()
บอกคุณในอดีตเท่านั้น หากคุณต้องการที่จะทราบว่าค่าการเปลี่ยนแปลงจริงคุณจะต้องทำการเปรียบเทียบที่เหมาะสมของและinserted
deleted