2016-08-29 3 views
2

트리거를 기반으로 여러 테이블에 대한 델타 추적을 만들려고합니다. 메신저 SQL 서버 2012를 사용하여. 내 질문을 단순화하기 위해, 방아쇠를 방아쇠 후 에서만보고 싶습니다. 역사와 일치하지 않는 레이아웃을 제외하고트리거를 사용한 델타 추적

CREATE TRIGGER [dbo].[T1_TraceUpdate] 
    on [dbo].[T1] 
    AFTER update 
AS 
BEGIN 
set nocount on; 
-- pseudo insert! 
insert into History select * from inserted; 
END 

:

CREATE TABLE [dbo].[History](
    [Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_History_Id] DEFAULT (newsequentialid()), 
    [ObjectId] [uniqueidentifier] NOT NULL, 
    [Timestamp] [datetime] NOT NULL CONSTRAINT [DF_History_Timestamp] DEFAULT (getdate()), 
    [ChangingUser] [varchar](max) NOT NULL CONSTRAINT [DF_History_ChangingUser] DEFAULT (suser_sname()), 
    [Column] [varchar](max) NOT NULL, 
    [OldValue] [nvarchar](max) NULL, 
    [NewValue] [nvarchar](max) NULL, 
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

지금 내가 추적하고 싶은 테이블에 트리거를 만들 것입니다 :

그래서 여기가 추적 테이블 표, 변경된 값과 변경되지 않은 값을 결정하는 데 문제가 있습니다. 의 dozends를 사용하는 것보다 더 똑똑한 방법으로 할 수 있습니까? (updated (col_name))? 또한 여러 행에 영향을 미칠 수 있음을 명심해야합니다. T1과 역사에 대한 샘플 데이터

T1의 디자인 :

CREATE TABLE [dbo].[T1](
    [Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_T1_Id] DEFAULT (newsequentialid()), 
    [Title] [text] NULL, 
    [Amount] [int] NULL, 
    [Price] [decimal](18, 7) NULL, 
    [Version] [timestamp] NOT NULL, 
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

샘플 는 (? 가능성도 커서없이) 더 많은 resuable 솔루션 ...

편집 # 1이 있습니까 데이터는 다음과 같이 수 :

:

1d118497-bf69-e611-9e7d-40a8f04d1095 Abc   3 37,2500000 
9cf095a8-bf69-e611-9e7d-40a8f04d1095 NULL   1 27,3000000 
cc38386d-fe69-e611-9e7d-40a8f04d1095 Storm Catcher 10 NULL  

바보 업데이 트를 불 수 있습니다 617,451,515,

update T1 set price = isnull(Price,100)*0.7 where Amount > 2 

역사는 이제 다음과 같을 것이다 :

4848D80B-4E73-E611-BD43-40A8F04D1095 1D118497-BF69-E611-9E7D-40A8F04D1095 2016-09-05 11:49:33.473 sa Price 37,2500000 26.0750000 
E80EAB18-4E73-E611-BD43-40A8F04D1095 CC38386D-FE69-E611-9E7D-40A8F04D1095 2016-09-05 11:49:33.473 sa Price NULL  70 
+1

먼저, 열을 지정하지 않고 콤보를 쓰거나 삽입하거나 선택하지 마십시오. 둘째,'COLUMNS_UPDATED()'를 사용하지 않는 것이 좋습니다. https://msdn.microsoft.com/en-au/library/ms186329.aspx. 이 정보를 저장하는 방법을 언급하지 않았습니다 –

+0

ofc 그 예입니다. 내 기록 테이블의 레이아웃이 내가 어떻게 그것을 저장하고 싶은지 보여줍니다. 사용자와 시간을 줄이는 것이 명백합니다. columns_updated()에서 볼 수 있듯이 동적 쿼리를 만들고 실행해야합니다. – Jaster

+0

사과 귀하의 게시물을 제대로 읽지 못하고 테이블을 놓쳤습니다. 어쨌든 열을 식별하고로드해야하기 때문에이 테이블에로드하려면 동적 SQL이 필요할 것입니다. 따라서 동적 SQL, 하드 코딩 된 SQL 또는 데이터베이스 미러링과 같은 하위 수준 복제 시스템을 원하는지 결정하십시오. 하드 코딩 된 SQL을 사용하면 실제로 자동 생성 할 수 있습니다. 테이블을 검사하고 올바른 생성 트리거 문 ... 코드 자동 생성과 같은 문자열을 내보내는 proc을 작성하십시오. –

답변

3

트리거가 이전과 을 한 후

로 사용할 수 있습니다 inserted and deleted 테이블, 테이블의 상태 사이의 차이를 찾기가 데이터 수정 및 그 차이에 따라 조치를 취하십시오.

업데이트 트랜잭션은 삭제 작업 다음에 삽입 작업과 유사합니다. 이전 행이 삭제 된 표에 먼저 복사되고 그리고 나서 새 행이 트리거 표와 삽입 표로 복사됩니다.

이러한 테이블의 열을 한 열씩 줄이고 비교하여 이전 값과 새 값이 다른 행만 History 테이블에 삽입하십시오.

CREATE TRIGGER [dbo].[T1_TraceUpdate] 
    on [dbo].[T1] 
    AFTER update 
AS 
BEGIN 
    set nocount on; 

    WITH 
    CTE_Inserted 
    AS 
    (
     SELECT ID, ColumnName, ColumnValue 
     FROM 
      inserted 
      CROSS APPLY 
      (VALUES 
       ('Title', Title), 
       ('Amount', CAST(Amount AS nvarchar(max))), 
       ('Price', CAST(Price AS nvarchar(max))) 
      ) AS V(ColumnName, ColumnValue) 
    ) 
    ,CTE_Deleted 
    AS 
    (
     SELECT ID, ColumnName, ColumnValue 
     FROM 
      deleted 
      CROSS APPLY 
      (VALUES 
       ('Title', Title), 
       ('Amount', CAST(Amount AS nvarchar(max))), 
       ('Price', CAST(Price AS nvarchar(max))) 
      ) AS V(ColumnName, ColumnValue) 
    ) 
    INSERT INTO dbo.History([ObjectId], [Column], [OldValue], [NewValue]) 
    SELECT 
     CTE_Inserted.Id AS [ObjectId] 
     ,CTE_Inserted.ColumnName 
     ,CTE_Deleted.ColumnValue AS [OldValue] 
     ,CTE_Inserted.ColumnValue AS [NewValue] 
    FROM 
     CTE_Inserted 
     INNER JOIN CTE_Deleted 
      ON CTE_Deleted.Id = CTE_Inserted.Id 
      AND CTE_Deleted.ColumnName = CTE_Inserted.ColumnName 
    WHERE 
     ISNULL(CTE_Inserted.ColumnValue, N'') <> ISNULL(CTE_Deleted.ColumnValue, N'') 
     OR (CTE_Inserted.ColumnValue IS NULL AND CTE_Deleted.ColumnValue IS NOT NULL) 
     OR (CTE_Inserted.ColumnValue IS NOT NULL AND CTE_Deleted.ColumnValue IS NULL) 
    ; 

END 

당신은 당신의 T1 표에 nvarchar(max) 형 대신 text을 사용해야합니다. 그리고 모든 값을 nvarchar(max) (또는 다른 유형의 경우 History 표에 여러 개의 열이 있음)으로 변환/변환해야합니다.

+0

이 작동하지만 모든 단일 테이블마다 모든 트리거에서 모든 열을 구체화 할 필요가없는보다 일반적인 솔루션을 찾고 있습니다. 보다 역동적이고 재기 가능한 솔루션을 원합니다. 여전히 교차 적용 +1입니다. – Jaster

+0

@Jaster, 관계형 데이터베이스는 열이 미리 알려지고 자주 변경되지 않는 안정된 스키마를 갖도록 설계되었습니다. "sp_executesql in trigger"를 빠르게 검색하면 'inserted'또는 'deleted'테이블을 참조하는 트리거에서 동적 SQL을 실행할 수 없음을 알 수 있습니다. 동적 SQL은 다른 별도의 컨텍스트에서 실행됩니다. 사람들은'inserted' 테이블을 임시 테이블에 복사하고 임시 테이블을 동적 SQL에서 참조하도록 제안합니다. 난 그것이 작동하는지 모르겠다 그리고 당신은 여전히 ​​어떻게 든 임시 테이블에 대한 열을 정의해야합니다. 나는'Change Data Capture' 기능에 대해 자세히 살펴볼 것입니다. –

+0

은 sql genearator가 필요합니다. 그런데 왜 id를 nvarchar로 캐스팅합니까? (해당 열은 추적 할 필요가 없습니다.) – Jaster

2

삽입, 선택 및 열에 참여하십시오. NULL 안전 비 - 동일성 비교에 유의하십시오. NULL이 아닌 값을 NULL로 설정하면 그 반대도 변경된 데이터로 계산됩니다.

CREATE TRIGGER [dbo].[T1_TraceUpdate] 
    on [dbo].[T1] 
    AFTER update 
AS 
BEGIN 
    set nocount on; 
    -- Changing Id breaks the logic of the trigger 
    IF UPDATE(ID) 
    BEGIN 
     RAISERROR ('ID cannot change.', 16, 1); 
     -- 
    END  
    INSERT INTO dbo.History(ObjectId, [Column], OldValue, NewValue) 
    SELECT inserted.ID, ColumnName, OldValue, NewValue 
    FROM inserted 
    INNER JOIN Deleted ON Deleted.Id = Inserted.Id 
    CROSS APPLY 
     (SELECT ColumnName='Title', OldValue=deleted.Title, NewValue=inserted.Title 
      WHERE isnull(nullif(inserted.Title,deleted.Title), nullif(deleted.Title,inserted.Title)) IS NOT NULL 
     UNION ALL 
     SELECT 'Amount', CAST(deleted.Amount AS nvarchar(max)), CAST(inserted.Amount AS nvarchar(max)) 
      WHERE isnull(nullif(inserted.Amount,deleted.Amount), nullif(deleted.Amount,inserted.Amount)) IS NOT NULL 
     UNION ALL 
     SELECT 'Price', CAST(deleted.Price AS nvarchar(max)), CAST(inserted.Price AS nvarchar(max)) 
      WHERE isnull(nullif(inserted.Price, deleted.Price), nullif(deleted.Price,inserted.Price)) IS NOT NULL 
     UNION ALL 
     -- you may want to skip version (`timestamp` type) as it's always updated by server 
     SELECT 'Version', CONVERT(varchar(max),CONVERT(VARBINARY,deleted.Version),1), CONVERT(varchar(max),CONVERT(VARBINARY,inserted.Version),1) 
      WHERE isnull(nullif(inserted.Version, deleted.Version), nullif(deleted.Version,inserted.Version)) IS NOT NULL 
    ) t; 
END; 
+0

더 일반적인 솔루션을 찾고 있는데, 모든 단일 열을 지정할 필요없이 여러 테이블에 applieable. 올바른 null 검사 및 버전 열 이해 +1. – Jaster

+0

필자는 SQLCLR 트리거를 생각해 볼 수 있습니다. – Serg

1

독자적인 솔루션을 제공 할 수는 없지만 접근 방법을 제안 할 수 있습니다. 시스템 테이블의 지정된 테이블에 대한 열을 검색 한 다음 동적 SQl을 루프로 사용하여 트리거 코드를 생성하여 모든 열을 한 번에 하나씩 가져 오는 스크립트를 작성했습니다.

테이블의 구조가 변경되거나 감사 할 새 테이블이 만들어 질 때마다이 스크립트를 실행합니다. 따라서 실제 트리거는 게시 된 다른 솔루션과 다소 비슷하지만 동적 SQL을 사용하여 모든 열을 통과하고 스크립트에 트리거를 추가하는 스크립트를 통해 생성됩니다.

손으로 각 방아쇠를 생성하는 것보다 빠르고 실수가없는 무언가가 있습니다. 이 접근 방식은 기존 데이터베이스에서 15 년 이상 완벽하게 작동했습니다. 일을 더 쉽게 이해하기 위해 삽입을위한 트리거, 삭제를위한 트리거 및 각 테이블에 대한 업데이트를위한 트리거가 있습니다.

+0

나는 이것이 블라디미르에 대한 대답에 기초하여 이미 내가 향하고있는 곳이라고 생각한다 ... 필요한 전환에 대해 좀 더 통찰력이 필요하다. – Jaster

+1

@Jaster, [DDL Trigger] (https://msdn.microsoft.com/en-AU/library/ms175941.aspx)에 DML 트리거의 코드를 생성하는이 동적 SQL 코드를 넣으려고합니다. 따라서 데이터 변경 사항을 기록하는 기본 DML 트리거가 항상 테이블 스키마와 동기화되도록 보장합니다. DDL 트리거 내에서 DML 트리거를 'ALTER'또는 'DROP/CREATE'할 수 있는지 여부는 알 수 없습니다. –

3

Jaster, 마지막으로 귀하의 검색어에 대한 해결책이 있습니다.

동적 스크립트로 필요한 테이블의 이름을 말할 수 있습니다. 스크립트는이 테이블에서 INSERT/UPDATE/DELETE 작업에 발생하는 동적 트리거를 만들어 모든 열 (데이터 유형이 text, ntext 및 image 인 열 제외)에 걸쳐 언급 된 테이블 테이블의 데이터 변경을 관리합니다. 요구 사항에 따라 작업을 변경할 수 있습니다. TableNameModifiedDateHISTORY 표에 포함되어있어 쉽게 찾을 수 있습니다.

다음은 솔루션 단계입니다. 아래 임시 테이블에서 제공하는 정보

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'History') 
CREATE TABLE [dbo].[History](
    [Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_History_Id] DEFAULT (newsequentialid()), 
    [Tablename] VARCHAR(150), 
    [ObjectId] VARCHAR(1500) NOT NULL, 
    [Timestamp] [datetime] NOT NULL CONSTRAINT [DF_History_Timestamp] DEFAULT (getdate()), 
    [ChangingUser] [varchar](max) NOT NULL CONSTRAINT [DF_History_ChangingUser] DEFAULT (suser_sname()), 
    [Column] [varchar](max) NOT NULL, 
    [OldValue] [nvarchar](max) NULL, 
    [NewValue] [nvarchar](max) NULL, 
     [ModifiedDate] Datetime NULL 
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'T1') 

BEGIN 
CREATE TABLE [dbo].[T1](
    [Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_T1_Id] DEFAULT (newsequentialid()), 
    [Title] text NULL, 
    [Amount] [int] NULL, 
    [Price] [decimal](18, 7) NULL, 
    [Version] [datetime] NOT NULL, 
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

INSERT INTO [T1] ([Title],[Amount],[Price],[Version]) VALUES ('Abc',3,372500000 ,'2016-09-01 11:49:33.473'), 
             (NULL,1,273000000 ,'2016-09-02 11:49:33.473'), 
             ('Storm Catcher',10,NULL,'2016-09-01 11:49:33.473') 
END 

를 사용하여 샘플 테스트 환경을 만들기

, 당신은 데이터 변경 사항을 추적하고 싶어하는 테이블 이름을 삽입해야합니다.

IF OBJECT_ID('tempdb..#FiltedTableList') IS NOT NULL 
DROP TABLE #FiltedTableList 
GO 

CREATE TABLE #FiltedTableList 
(ID INT IDENTITY(1,1), 
    TableName VARCHAR(150)) 
INSERT INTO #FiltedTableList (TableName) 
SELECT 'T1' --here we have only one table to track the changes 

여기 마지막 단계가됩니다. 아래 동적 쿼리를 실행하십시오. 그래서 여기

DECLARE @TableName sysname,@Counter INT,@Dynamsql varchar(MAX) 
SELECT @Counter=MAX(ID) FROM #FiltedTableList 

SET NOCOUNT ON 

WHILE (@Counter !=0) 

BEGIN 

     SELECT @TableName= TableName 
     FROM #FiltedTableList 
     WHERE [email protected] 

     DECLARE @ColumnFilter VARCHAR (MAX) 

     SELECT @ColumnFilter=Stuff(( Select ', ' + C.COLUMN_NAME 
     From INFORMATION_SCHEMA.COLUMNS As C Where C.TABLE_NAME = @TableName 
       AND c.DATA_TYPE NOT IN ('text','ntext','image') 
       Order By C.ORDINAL_POSITION For Xml Path('') ), 1, 2, '') 


     IF OBJECT_ID('tempdb..##MagInserted') IS NOT NULL 
     DROP TABLE ##MagInserted 

     IF OBJECT_ID('tempdb..##MagDeleted') IS NOT NULL 
     DROP TABLE ##MagDeleted 


EXEC('IF OBJECT_ID (''' + @TableName+ '_LogTracker'', ''TR'') IS NOT NULL DROP TRIGGER ' + @TableName+ '_LogTracker') 
SELECT @Dynamsql = 
'CREATE TRIGGER ' + @TableName+ '_LogTracker ON ' + @TableName+ ' FOR INSERT, UPDATE, DELETE 
    AS 
    DECLARE @column int , @maxColumn int , @char int,@columnname varchar(128) , @TableName varchar(128) , @KeyColumn varchar(1000) , @Dynamsql varchar(2000) ,@dataquery VARCHAR(MAX), 
      @ModifiedDate varchar(21), @UserName varchar(128) , @key int , @Type char(1) , @PKFieldSelect varchar(1000),@PKValueSelect varchar(1000) 
    SELECT @TableName = ''' + @TableName+ '''; 
    SELECT @UserName = system_user , @ModifiedDate = convert(varchar(8), getdate(), 112) + '' '' + convert(varchar(12), getdate(), 114); 
    SELECT '+ @ColumnFilter+' INTO ##MagInserted FROM inserted;SELECT '+ @ColumnFilter+' INTO ##MagDeleted FROM deleted; 
    SELECT @KeyColumn = COALESCE(@KeyColumn + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME 
    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME 
    WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY'' 
    SELECT @PKFieldSelect = COALESCE(@PKFieldSelect+''+'','''') + '''''''' + COLUMN_NAME + '''''''' 
    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME 
    WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY'' 
    SELECT @PKValueSelect = coalesce(@PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))'' 
    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME 
    WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY'' 
    SELECT @column = 0 , @maxColumn = max(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName 
       WHILE @column < @maxColumn 
       BEGIN 
        SELECT @column = min(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName AND ORDINAL_POSITION > @column 
        SELECT @key = (@column - 1)% 8 + 1; SELECT @key = power(2,@key - 1);SELECT @char = ((@column - 1)/8) + 1 
        IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @key > 0 OR @Type IN (''I'',''D'') 
          BEGIN 
          SELECT @columnname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName and ORDINAL_POSITION = @column 
          SELECT @Dynamsql = ''insert History (TableName,[ObjectId], [Column], OldValue, NewValue, ModifiedDate, ChangingUser)'' 
          SELECT @Dynamsql = @Dynamsql + '' select '''''' + @TableName + '''''''' 
          SELECT @Dynamsql = @Dynamsql + '','' + @PKValueSelect 
          SELECT @Dynamsql = @Dynamsql + '','''''' + @columnname + '''''''' 
          SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),d.'' + @columnname + '')'' 
          SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),i.'' + @columnname + '')'' 
          SELECT @Dynamsql = @Dynamsql + '','''''' + @ModifiedDate + '''''''' 
          SELECT @Dynamsql = @Dynamsql + '','''''' + @UserName + '''''''' 
          SELECT @Dynamsql = @Dynamsql + '' from ##MagInserted i full outer join ##MagDeleted d'' 
          SELECT @Dynamsql = @Dynamsql + @KeyColumn 
          SELECT @Dynamsql = @Dynamsql + '' where i.'' + @columnname + '' <> d.'' + @columnname 
          SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is null and d.'' + @columnname + '' is not null)'' 
          SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is not null and d.'' + @columnname + '' is null)'' 
          EXEC (@Dynamsql) 
          END END ' 
SELECT @Dynamsql 
EXEC(@Dynamsql) 

SET @[email protected] 

END 

테스트 케이스 및 출력 :

enter image description here

+0

스마트 솔루션이지만 트리거를 생성하는 동적 스크립트를 찾고 있지 않습니다. 내 탐구는 트리거의 일부로 동적 SQL에 관한 것이 었습니다. 이는 옵션이 아닌 것으로 보입니다. – Jaster

1
또한 CDC (변경 데이터 캡처)를 사용할 수 있습니다

  1. 테이블을 사용
  2. CDC
  3. 에 대한 DB를 사용/CDC에 대한 열
  4. 변경 사항을 읽을 CDC 테이블 읽기 (각각의 실제 변경에는 이전 행과 새로운 행이 있습니다. + $ updatemask는 업데이트 된 열을 찾을 수있는 곳입니다. 가짜 업데이트를 자동으로 제외합니다.)

이러한 모든 작업은 각 테이블마다 동적으로 수행 할 수 있습니다.

트리거가 OLTP 시스템을 느리게 만듭니다. 트리거는 완료 될 때까지 트랜잭션이 커밋되지 않습니다. CDC는 트랜잭션이 커밋되고 원하는 시점에 작동합니다. CDC 스캐너는 트랜잭션 복제 에서처럼 트랜잭션 로그를 직접 읽습니다. CDC의 장점은 기본 테이블이 아닌 CDC 테이블을 읽는 것입니다.조용한 시간에 프로세스를 지연하고 기록 테이블을 읽거나 채울 수 있습니다 (예 : 주말이나 하룻밤.

+0

cdc 프로세스 중에 작성된 데이터를 수정할 수 있습니까? 예 : 사용자/시간을 변경 하시겠습니까? – Jaster

+0

cdc 테이블을 변경 하시겠습니까? 예. CDC 데이터를 변경할 수 있습니다. CDC 스캐너는 트랜잭션 로그를 읽고 CDC 테이블을 채 웁니다. 그런 다음 해당 테이블에 대해 DML 명령을 수행 할 수 있지만 CDC 테이블을 읽고 히스토리 테이블을 채우는 데 필요하지 않습니다. – Anton

관련 문제