SQL Change Data Capture
重要資料表要做差異 Log 檔,可以不用手刻,有方便的作法。
過去手刻作法
過去筆者針對系統中主要的資料表要做交易歷程功能,作法是在資料庫中新增結構一樣的資料表,並賦予另一個自動序號欄位或時間欄位做為 Primary Key,接著撰寫一個 Trigger 當原本資料表新增、修改、刪除時另外抄寫到 Log 資料表。其實可以更容易的
近期在做兩地資料同步時,發現早在 SQL Server 2008 時就有提供兩種解決方案:Change Data Capture 與 Change Tracking,兩者比較可參考 Comparing Change Data Capture and Change Tracking。筆者採用 Change Data Capture (以下簡稱 CDC) 取代上述原本資料歷程手動的做法,CDC 可以記錄執行 DML 時資料當時的原貌, 並可以區分資料被異動狀態 (新增、修改前、修改後、刪除),當然也可以查到資料被刪除前最後儲存的值。
以 Pub 資料庫中的 jobs 資料表為例,在資料庫與指定某一資料表啟用 CDC 功能,語法:
use [MyDataBase]; /*新增一個交易資料表*/ CREATE TABLE dbo.MyTable( ID int IDENTITY(1,1) NOT NULL, MyCode varchar(4) NULL, DESCRIP varchar(128) NULL, CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED(ID ASC)); /*資料庫啟用 CDC*/ EXEC sys.sp_cdc_enable_db; /*單一資料表 dbo.MyTable 啟用 CDC*/ EXEC sys.sp_cdc_enable_table @source_schema='dbo', @source_name='MyTable', @role_name='cdc_Admin', @capture_instance='dbo_MyTable', @supports_net_changes=1;
完成後,每指定一個資料表啟用 CDC 就會建立兩個相對的函式
PS: CDC 需要 SQL Server Agent 服務啟用下才能正常運作。
在原本的資料表進行測試新增、修改、刪除等,可以查詢CDC資料表會有所有歷程,若是資料更新,則會新增兩筆資料分別是更新前與更新後。
select * from cdc.dbo_MyTable_CT order by 1;
這個異動資料表上有原本資料表所有的欄位,另外有幾個欄位
- _$start_lsn -- commit log sequence number (LSN) within the same Transaction
- _$end_lsn -
- _$seqval -- order changes within a transaction
- _$operation -- 1=delete, 2=insert,3=updatebefore,4=updateafter
- _$update_mask -- for insert,delete all bits are set, for update bits set correspond to columns changed
/*測試異動資料 */
insert into dbo.MyTable(MyCode, DESCRIP) values ('1000', '1000-DESCRIP');
update dbo.MyTable set MyCode='1001', DESCRIP='ABC' where ID = 1;
/*以 CDC 函數查詢*/
DECLARE @from_lsn binary(10), @to_lsn binary(10), @row_filter_option nvarchar(30)
SET @from_lsn = [sys].[fn_cdc_get_min_lsn]('dbo_MyTable')
SET @to_lsn = [sys].[fn_cdc_get_max_lsn]()
SET @row_filter_option = 'all' --'all, 'all update old'
/*查出所有歷程*/
select * from [cdc].[fn_cdc_get_all_changes_dbo_MyTable](@from_lsn, @to_lsn, @row_filter_option)
/*查出最後狀態*/
select * from [cdc].[fn_cdc_get_net_changes_dbo_MyTable](@from_lsn, @to_lsn, @row_filter_option)
/*單一資料表 cox.CUSREP 取消 CDC*/ exec sys.sp_cdc_disable_table @source_schema='cox', @source_name='CUSREP', @capture_instance='cox_CUSREP'; /*資料庫停用 CDC*/ exec sys.sp_cdc_disable_db;
CDC 參考 T-SQL 範例
筆者應用在異地資料庫同步上,定時讀取出需要做同步資料的查詢語法DECLARE @from_lsn binary(10), @to_lsn binary(10), @row_filter_option nvarchar(30)
SET @from_lsn = [sys].[fn_cdc_get_min_lsn]('dbo_jobs')
SET @to_lsn = [sys].[fn_cdc_get_max_lsn]()
SET @row_filter_option = 'all'--'all, 'all with merge', 'all with mask'
--DECLARE ChangeSet_cursor CURSOR FOR
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]
from [cdc].[fn_cdc_get_net_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=2
union
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]
from [cdc].[fn_cdc_get_net_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=4
union
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]from [cdc].[fn_cdc_get_all_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=1
order by __$start_lsn
;
預存程序範例 - 讀取所有異動記錄,區分新增、修改、刪除做處理,並清除本次所有異動記錄。
/*--------------------------------------------------
description: 讀取 jobs 異動記錄,
author: Robin
date: 2014/12/18
testing code:
--------------------------------------------------
update jobs set job_desc=job_desc+' *' where job_id=1
EXEC SYNC_Changes_jobs;
SELECT * FROM [cdc].[dbo_jobs_CT];
--------------------------------------------------*/
CREATE Procedure [dbo].[SYNC_Changes_jobs]
AS
BEGIN TRAN;
DECLARE @start_lsn binary(10), @operation int,
@job_id smallint,
@job_desc varchar(50),
@min_lvl tinyint,
@max_lvl tinyint
DECLARE @from_lsn binary(10), @to_lsn binary(10), @row_filter_option nvarchar(30)
SET @from_lsn = [sys].[fn_cdc_get_min_lsn]('dbo_jobs')
SET @to_lsn = [sys].[fn_cdc_get_max_lsn]()
SET @row_filter_option = 'all'--'all, 'all with merge', 'all with mask'
DECLARE ChangeSet_cursor CURSOR FOR
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]
from [cdc].[fn_cdc_get_net_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=2
union
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]
from [cdc].[fn_cdc_get_net_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=4
union
select __$start_lsn, __$operation
, [job_id],[job_desc],[min_lvl],[max_lvl]from [cdc].[fn_cdc_get_all_changes_dbo_jobs](@from_lsn, @to_lsn, @row_filter_option)
where __$operation=1
order by __$start_lsn
;
OPEN ChangeSet_cursor
FETCH NEXT FROM ChangeSet_cursor
INTO @start_lsn, @operation, @job_id,@job_desc, @min_lvl, @max_lvl;
WHILE @@FETCH_STATUS = 0
BEGIN
IF @operation=2
BEGIN
PRINT CONVERT(varchar(10), @job_id) + ' ' + @job_desc + ' --- 新增'
END
IF @operation=4
BEGIN
PRINT CONVERT(varchar(10), @job_id) + ' ' + @job_desc + ' --- 異動'
END
IF @operation=1
BEGIN
PRINT CONVERT(varchar(10), @job_id) + ' ' + @job_desc + ' --- 刪除'
END
FETCH NEXT FROM ChangeSet_cursor
INTO @start_lsn, @operation, @job_id,@job_desc, @min_lvl, @max_lvl;
END
CLOSE ChangeSet_cursor;
DEALLOCATE ChangeSet_cursor;
COMMIT TRAN;
delete from [cdc].[dbo_jobs_CT];
最後若要取消 CDC 功能
/*單一資料表 dbo.MyTable 取消 CDC*/ exec sys.sp_cdc_disable_table @source_schema='dbo', @source_name='MyTable', @capture_instance='dbo_MyTable'; /*整個資料庫停用*/ exec sys.sp_cdc_disable_db;
相關連結
- How to, Step by Step Change Data Capture (CDC) Tutorial
- About Change Data Capture (SQL Server)
- Change Data Capture Stored Procedures (Transact-SQL)
- Tuning the Performance of Change Data Capture in SQL Server 2008
- Comparing Change Data Capture and Change Tracking
- Change Tracker Tutorial
- [SQL SERVER][SSIS]利用CDC實現資料差異增量更新



