CHECKSUM()
실제 값을 비교하여 값이 변경되었는지 확인하는 매우 간단한 방법으로 사용할 수 있습니다 . CHECKSUM()
전달 된 값 목록에 걸쳐 체크섬을 생성하며 그 중 숫자와 유형이 결정되지 않습니다. 이와 같은 체크섬을 비교할 가능성이 적다는 점에 유의하십시오. 이를 처리 할 수 없으면 1을HASHBYTES
대신 사용할 수 있습니다 .
아래 예는 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()
열이 수정되는 경우 함수가 주어진 열 비트에 대한 true를 돌려 ANY 에 의해 영향을받는 행 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
표는 지금은 (해당 테이블의 두 열을) 전혀 변화를 보여주지 이후 속해 그렇지 않은과 같이 몇 가지 잠재적으로 오해의 소지가 행이 있습니다. 아래 이미지의 두 번째 행 집합에서 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- HASHBYTES 계산으로 인해 충돌이 발생할 수있는 작은 기회에 대한 설명 은 /programming/297960/hash-collision-what-are-the-chances 를 참조 하십시오 . Preshing 은이 문제에 대한 적절한 분석을 가지고 있습니다.
SET
목록에 언급되어 있는지 또는 실제로 값이 변경되었는지 알고 싶 습니까? 모두UPDATE
와COLUMNS_UPDATED()
단지 당신에게 전자를 말한다. 당신이 값이 실제로 변경 알고 싶은 경우에, 당신은의 적절한 비교를해야inserted
하고deleted
.