「變更數據捕獲」(CDC)功能

http://www.cnblogs.com/downmoon/archive/2012/04/10/2439462.html

SQL Server 2008中SQL應用系列--目錄索引

  本文主要介紹SQL Server中記錄數據變更的四個方法:觸發器、Output子句、變更數據捕獲(Change Data Capture 即CDC)功能、同步更改跟蹤。其中後兩個爲SQL Server 2008所新增。

一、觸發器

  在SQL Server的早期版本中,如果要記錄某個表或視圖的Insert/Update/Delete操作,我們可以藉助觸發器(Trigger)(http://msdn.microsoft.com/zh-cn/library/ms189799.aspx),這在數據量較小的情況下往往是有效的方式之一,其中後觸發器(After Trigger)只能跟蹤表的三個操作中的任意組合,而前觸發器(Instead Of trigger)可以處理表和視圖的更新(即使普通的Update View語句在某些列不明確的情況下報錯)。我們看兩個例子:

  準備基礎數據:

複製代碼
USE testDb2
GO
--創建兩個測試表
IF NOT OBJECT_ID('DepartDemo') IS NULL
DROP TABLE [DepartDemo]
GO
IF NOT OBJECT_ID('DepartChangeLogs') IS NULL
DROP TABLE [DepartChangeLogs]
GO
--測試表
CREATE TABLE [dbo].[DepartDemo](
[DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,
[DName] [nvarchar](200) NULL,
[DCode] [nvarchar](500) NULL,
[Manager] [nvarchar](50) NULL,
[ParentID] [int] NOT NULL DEFAULT ((0)),
[AddUser] [nvarchar](50) NULL,
[AddTime] [datetime] NULL,
[ModUser] [nvarchar](50) NULL,
[ModTime] [datetime] NULL,
[CurState] [smallint] NOT NULL DEFAULT ((0)),
[Remark] [nvarchar](500) NULL,
[F1] [int] NOT NULL DEFAULT ((0)),
[F2] [nvarchar](300) NULL
)
GO
--記錄日誌表
CREATE TABLE [DepartChangeLogs]
([LogID] [bigint] IDENTITY(1001,1) NOT NULL PRIMARY KEY,
[DID] [int] NOT NULL,
[DName] [nvarchar](200) NULL,
[DCode] [nvarchar](500) NULL,
[Manager] [nvarchar](50) NULL,
[ParentID] [int] NOT NULL DEFAULT ((0)),
[AddUser] [nvarchar](50) NULL,
[AddTime] [datetime] NULL,
[ModUser] [nvarchar](50) NULL,
[ModTime] [datetime] NULL,
[CurState] [smallint] NOT NULL DEFAULT ((0)),
[Remark] [nvarchar](500) NULL,
[F1] [int] NOT NULL DEFAULT ((0)),
[F2] [nvarchar](300) NULL,
[LogTime] DateTime Default(Getdate()) Not Null,
[InsOrUpd] char not null
)
GO
複製代碼

  創建觸發器:

複製代碼
/*******   創建一個After DML觸發器  ******/
/*********  [email protected]  邀月***************/
CREATE TRIGGER dbo.tri_LogDepartDemo
ON [dbo].[DepartDemo]
AFTER INSERT, Delete  /************此處使用update與「Insert,Delete」效果是一樣的,邀月注  **********/
AS
SET NOCOUNT ON   --屏蔽觸發器發送「受影響的行數」給應用程序

-- Inserted rows
INSERT [DepartChangeLogs]
(DID,[DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],
 LogTime, InsOrUPD)
SELECT DISTINCT DID,[DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],
 GETDATE(), 'I'
FROM inserted i

-- Deleted rows
INSERT [DepartChangeLogs]
(DID,[DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],
 LogTime, InsOrUPD)
SELECT DISTINCT DID,[DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],
 GETDATE(), 'D'
FROM deleted d
GO

INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])
VALUES (N'國家統計局房產審計一科', N'0', N'胡不歸', 0, N'DeomUser',
 CAST(0x00009DF7017B6F96 AS DateTime), N'', CAST(0x0000000000000000 AS DateTime),
 1, N'專業評估全國房價,爲老百姓謀福祉', 0, N'')
GO

----該Update不會被觸發器記錄,但Update會生效
UPDATE departDemo SET [Manager]='任我行' WHERE DID=101
GO

DELETE FROM departDemo where DID=101
GO

SELECT * FROM [DepartChangeLogs]
複製代碼

  統計效果:
邀月工作室
如果你覺得觸發器過於浪費,你可以試着根據某些字段以縮小觸發器的範圍

複製代碼
/********* 使用DML觸發器記錄特定列的修改 ***/
/*********  [email protected]  邀月***************/
CREATE TRIGGER dbo.[tri_LogDepartDemo2]
ON [dbo].[DepartDemo]
AFTER Update
AS
IF Update([Manager])
    Begin
        print '該部門主管實行終身任免制,不得中途更改!'
        Rollback ----回滾Update操作
    End

GO
UPDATE departDemo SET [Manager]='任我行' WHERE DID=101
GO
複製代碼

  執行結果:


邀月工作室
  但觸發器的缺陷也是顯而易見的,使用觸發器請注意以下幾點:

1、觸發器通常很隱蔽,換句話說,易忘記,特別在檢查性能和邏輯問題時。

2、長時間運行的觸發器會嚴重減慢數據操作,特別是在數據頻繁修改的數據庫中。

3、不記錄日誌的更新不會引起DML觸發器的觸發(如WRITETEXT、Trunacte table及批量插入操作)。

4、約束通常比觸發器運行更快。

5、處理某些邏輯時,存儲過程通常比觸發器要更易維護和管理。

6、不允許在觸發器中使用Select返回結果集。

  關於觸發器的更多內容,請看MSDN(http://msdn.microsoft.com/zh-cn/library/ms189799.aspx

 

二、使用Output子句

  官方解釋:OutPut子句(http://technet.microsoft.com/zh-cn/library/ms177564.aspx返回受 INSERT、UPDATE、DELETE 或 MERGE 語句影響的各行中的信息,或返回基於受這些語句影響的各行的表達式。 這些結果可以返回到處理應用程序,以供在確認消息、存檔以及其他類似的應用程序要求中使用。 也可以將這些結果插入表或表變量。 另外,您可以捕獲嵌入的 INSERT、UPDATE、DELETE 或 MERGE 語句中 OUTPUT 子句的結果,然後將這些結果插入目標表或視圖。

 舉例:

複製代碼
 /********* 使用Output記錄表記錄的修改 *****/
/*********  [email protected]  邀月***************/

----刪除前面的觸發器
Drop TRIGGER dbo.[tri_LogDepartDemo]
DROP TRIGGER dbo.[tri_LogDepartDemo2]

INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])
OUTPUT Inserted.*,getdate(),'I' ---注意這行是新增的
INTO DepartChangeLogs           ---注意這行是新增的
VALUES (N'發改委', N'0', N'向問天', 0, N'DeomUser',
 CAST(0x00009DF7017B6F96 AS DateTime), N'', CAST(0x0000000000000000 AS DateTime),
 1, N'油價,我說了算', 0, N'')
GO

SELECT * FROM [DepartChangeLogs]
複製代碼

邀月工作室

  注意:

  1、從OUTPUT 中返回的列反映 INSERT、UPDATE 或 DELETE 語句完成之後但在觸發器執行之前的數據。

  2、SQL Server 並不保證由使用 OUTPUT 子句的 DML 語句處理和返回行的順序。

  3、與觸發器相比,OutPut子句可以直接處理Merge語句。

   以上兩種方法各有千秋,在合適的情況下采取合適的方法纔是明智的選擇,令人驚喜的是,SQL Server 2008起,爲我們提供了更爲強大的內建的方法-變更數據捕獲(CDC,http://msdn.microsoft.com/zh-cn/library/bb500244%28v=sql.100%29.aspx)和更改跟蹤,下面我們隆重介紹它們。

三、使用「變更數據捕獲」(CDC)功能

  SQL Server 2008提供了內建的方法變更數據捕獲(Change Data Capture 即CDC)以實現異步跟蹤用戶表的數據修改,而且這一功能擁有最小的性能開銷。可以用於其他數據源的持續更新,例如將OLTP數據庫中的數據變更遷移到數據倉庫數據庫。

  要使用CDC功能,首先我們得在數據庫中啓用該功能。在此我們沿用上例中使用的數據庫Testdb2

複製代碼
/**************異步跟蹤數據更新演示*************/
/************* [email protected] 邀月***************/
use master
GO

IF EXISTS (SELECT [name] FROM sys.databases WHERE name = 'TestDb2')
drop DATABASE TestDb2
Go
CREATE DATABASE TestDb2
GO

--查看是否啓用CDC
SELECT is_cdc_enabled FROM sys.databases WHERE name = 'TestDb2'

USE TestDb2
GO
----啓用當前數據庫的CDC功能
EXEC sys.sp_cdc_enable_db
GO
複製代碼

  如果報15517錯誤,請換用其他owner,邀月注

複製代碼
SELECT is_cdc_enabled FROM sys.databases WHERE name = 'TestDb2'

/*
is_cdc_enabled
1
*/
USE testDb2
GO

CREATE TABLE [dbo].[DepartDemo](
[DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,
[DName] [nvarchar](200) NULL,
[DCode] [nvarchar](500) NULL,
[Manager] [nvarchar](50) NULL,
[ParentID] [int] NOT NULL DEFAULT ((0)),
[AddUser] [nvarchar](50) NULL,
[AddTime] [datetime] NULL,
[ModUser] [nvarchar](50) NULL,
[ModTime] [datetime] NULL,
[CurState] [smallint] NOT NULL DEFAULT ((0)),
[Remark] [nvarchar](500) NULL,
[F1] [int] NOT NULL DEFAULT ((0)),
[F2] [nvarchar](300) NULL
)
GO

/**********************************
需要啓用SQL Server Agent服務,否則會報錯,邀月注
SQLServerAgent is not currently running so it cannot be notified of this action.
***********************************/

/****** 捕獲所有的行變更,只返回行的淨變更,其他默認 *******/
EXEC sys.sp_cdc_enable_table
@source_schema = 'dbo',
@source_name = 'DepartDemo',
@role_name = NULL,
@capture_instance = NULL,
@supports_net_changes = 1,
@index_name = NULL,
@captured_column_list = NULL,
@filegroup_name = default
複製代碼

  注意此時,SQL Server 自啓動了兩個job,一個捕獲,一個清除,注意清除是默認凌晨2點,清除72小時以上的數據。如果同一數據庫的表中CDC已經啓用,不會重建job。

複製代碼
/*
Job 'cdc.TestDb2_capture' started successfully.
Job 'cdc.TestDb2_cleanup' started successfully.
*/

--確認表已經被跟蹤
SELECT is_tracked_by_cdc FROM sys.tables
WHERE name = 'DepartDemo' and schema_id = SCHEMA_ID('dbo')
/*
is_tracked_by_cdc
1
*/

--確認
EXEC sys.sp_cdc_help_change_data_capture 'dbo', 'DepartDemo'
複製代碼

邀月工作室


可以看到,SQL Server 增加了一個表[cdc].[dbo_DepartDemo_CT] 
相比源表多了個字段: 
[__$start_lsn] 
,[__$end_lsn] 
,[__$seqval] 
,[__$operation] 
,[__$update_mask] 

邀月工作室

不建議直接查詢該表,而應該使用下面的技巧

複製代碼
USE TestDb2
GO

INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])
VALUES (N'銀監會', N'0', N'雲中鶴', 0, N'DemoUser1',
CAST(0x00009DF7017B6F96 AS DateTime), N'', CAST(0x0000000000000000 AS DateTime),
1, N'監管匯率', 0, N'')

INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])
VALUES (N'統計局', N'0', N'神算子', 0, N'DemoUser2',
CAST(0x00009DF7017B6F96 AS DateTime), N'', CAST(0x0000000000000000 AS DateTime),
1, N'統計數據', 0, N'')
GO

UPDATE [dbo].[DepartDemo]
SET Manager='段正淳'
WHERE DID =101

DELETE [dbo].[DepartDemo]
WHERE DID = 102
複製代碼

  要查詢變更,我們需要藉助大名鼎鼎的日誌序列號(Log Sequence Numbers)即LSN(http://msdn.microsoft.com/zh-cn/library/ms190411%28v=sql.100%29.aspx)來實現LSN級別的跟蹤數據變更。 下面示例中sys.fn_cdc_map_time_to_lsn(http://msdn.microsoft.com/zh-cn/library/bb500137%28v=sql.100%29.aspx)用於LSN轉換爲時間。

複製代碼
/******* 使用LSN 查看CDC記錄 *********/

--http://msdn.microsoft.com/zh-cn/library/bb500137%28v=sql.100%29.aspx
SELECT sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30') GO

UPDATE [dbo].[DepartDemo]
SET Manager='段正淳'
WHERE DID =101

DELETE [dbo].[DepartDemo]
WHERE DID = 102
複製代碼

  要查詢變更,我們需要藉助大名鼎鼎的日誌序列號(Log Sequence Numbers)即LSN(http://msdn.microsoft.com/zh-cn/library/ms190411%28v=sql.100%29.aspx)來實現LSN級別的跟蹤數據變更。 下面示例中sys.fn_cdc_map_time_to_lsn(http://msdn.microsoft.com/zh-cn/library/bb500137%28v=sql.100%29.aspx)用於LSN轉換爲時間。

複製代碼
/******* 使用LSN 查看CDC記錄 *********/

--http://msdn.microsoft.com/zh-cn/library/bb500137%28v=sql.100%29.aspx
SELECT sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30') as BeginLSN

/*
BeginLSN
0x0000002C000000AA0003
*/

SELECT sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59') as EndLSN

/*
EndLSN
0x0000002C000001C20005
*/

/**************查看所有CDC記錄*************/
/************* [email protected] 邀月***************/

DECLARE @FromLSN varbinar
相關文章
相關標籤/搜索