SQL Server 2008中新增的变更数据捕获(CDC)和更改跟踪

SQL Server 2008中新增的变更数据捕获(CDC)和更改跟踪

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语句在某些列不明确的情况下报错)。我们看两个例子:

  准备基础数据:

View Code
复制代码
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') 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 varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30')

DECLARE @ToLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59')

SELECT
__$operation,
__$update_mask,
DID,
DName,
Manager
FROM [cdc].[fn_cdc_get_all_changes_dbo_DepartDemo]
(@FromLSN, @ToLSN, 'all')

/************查看所有更新*************************

__$operation __$update_mask DID DName Manager
2 0x1FFF 105 银监会 云中鹤
2 0x1FFF 106 统计局 神算子
1 0x1FFF 101 银监会 段正淳
1 0x1FFF 103 银监会 云中鹤
1 0x1FFF 104 统计局 神算子
1 0x1FFF 105 银监会 云中鹤
1 0x1FFF 106 统计局 神算子
2 0x1FFF 107 银监会 云中鹤
2 0x1FFF 108 统计局 神算子
4 0x0008 107 银监会 段正淳
1 0x1FFF 108 统计局 神算子
*/

/**************查看所有CDC记录*************/
/************* [email protected] 邀月***************/
DECLARE @FromLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30')

DECLARE @ToLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59')

--解释一下Operation的具体含义
SELECT
CASE __$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END Operation,
__$update_mask,
DID,
DName,
Manager
FROM [cdc].[fn_cdc_get_all_changes_dbo_DepartDemo]
(@FromLSN, @ToLSN, 'all update old')
复制代码

邀月工作室

复制代码
/**************查看净更改(Net changes)CDC记录*************/
/************* [email protected] 邀月 ***************/

INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],
[AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])
VALUES (N'药监局', N'0', N'蝶谷医仙', 0, N'DemoUser3',
CAST(0x00009DF7017B6F96 AS DateTime), N'', CAST(0x0000000000000000 AS DateTime),
1, N'制定药价', 0, N'')
GO

UPDATE [dbo].[DepartDemo]
SET Manager='胡青牛'
WHERE DID =109

DECLARE @FromLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30')

DECLARE @ToLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59')

SELECT
CASE __$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
WHEN 5 THEN 'MERGE'
END Operation,
__$update_mask,
DID,
DName,
Manager
FROM [cdc].[fn_cdc_get_net_changes_dbo_DepartDemo]
(@FromLSN, @ToLSN, 'all with mask')
复制代码

邀月工作室

  我们还可以通过转换CDC更新掩码获得更为直观的结果,这里需要借助于另外两个函数sys.fn_cdc_is_bit_set(http://msdn.microsoft.com/zh-cn/library/bb500241%28v=SQL.110%29.aspx)和sys.fn_cdc_get_column_ordinal(http://msdn.microsoft.com/zh-cn/library/bb522549%28v=SQL.100%29.aspx)

 

复制代码
/************** 转换CDC更新掩码 *************/
/************* [email protected] 邀月 **************/

UPDATE dbo.[DepartDemo]
SET [Manager] = '东方不败'
WHERE DID =107

UPDATE dbo.[DepartDemo]
SET ParentID = 109
WHERE DID =107

DECLARE @FromLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 16:09:30')

DECLARE @ToLSN varbinary(10) =
sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59')

SELECT
sys.fn_cdc_is_bit_set (
sys.fn_cdc_get_column_ordinal (
'dbo_DepartDemo' , 'Manager' ),
__$update_mask) Manager_Updated,
sys.fn_cdc_is_bit_set (
sys.fn_cdc_get_column_ordinal (
'dbo_DepartDemo' , 'ParentID' ),
__$update_mask) ParentID_Updated,
DID,
Manager,
ParentID
FROM cdc.fn_cdc_get_all_changes_dbo_DepartDemo
(@FromLSN, @ToLSN, 'all')
WHERE __$operation = 4
复制代码

 

邀月工作室

  除了前面介绍的指定LSN边界的方法,SQL Server还提供了一系列的获取边界的方法:

sys.fn_cdc_get_max_lsn(http://msdn.microsoft.com/zh-cn/library/bb500304%28v=sql.100%29.aspx)

sys.fn_cdc_get_min_lsn(http://msdn.microsoft.com/zh-cn/library/bb510621%28v=sql.100%29.aspx)

sys.fn_cdc_increment_lsn(http://msdn.microsoft.com/zh-cn/library/bb510745%28v=sql.100%29.aspx)

sys.fn_cdc_decrement_lsn(http://msdn.microsoft.com/zh-cn/library/bb500246%28v=sql.100%29.aspx)

  示例如下:

复制代码
/************** 获取LSN边界的其他方法 *************/
/************* [email protected] 邀月 **************/

--获取最小边界
SELECT sys.fn_cdc_get_min_lsn ('dbo_DepartDemo') Min_LSN
--获取可用的最大边界
SELECT sys.fn_cdc_get_max_lsn () Max_LSN
--获取最大边界的下一个序号
SELECT sys.fn_cdc_increment_lsn (sys.fn_cdc_get_max_lsn()) New_Lower_Bound_LSN
--获取最大边界的前一个序号
SELECT sys.fn_cdc_decrement_lsn (sys.fn_cdc_get_max_lsn())
New_Lower_Bound_Minus_one_LSN
复制代码

 邀月工作室

  通过以下存储过程在数据库和表级禁用CDC

sys.sp_cdc_disable_table (http://msdn.microsoft.com/zh-cn/library/bb510702(v=sql.100).aspx)

sys.sp_cdc_disable_db(http://msdn.microsoft.com/zh-cn/library/bb522508(v=sql.100).aspx)注意,该命令同时也删除了CDC架构和相关的SQL代理作业。

复制代码
/************** 在数据库和表级禁用CDC *************/
/************* [email protected] 邀月 **************/

EXEC sys.sp_cdc_disable_table 'dbo', 'DepartDemo', 'all'

SELECT is_tracked_by_cdc FROM sys.tables
WHERE name = 'DepartDemo' and schema_id = SCHEMA_ID('dbo')

--当前数据库上禁用CDC
EXEC sys.sp_cdc_disable_db
复制代码

 

四、使用“更改跟踪”以最小的磁盘开销跟踪净数据更改

  CDC可以用来对数据库和数据仓库的持续数据变更进行异步数据跟踪,而SQL Server 2008中新增的“更改跟踪”却是一个同步进程,是DML操作本身(I/D/U)事务的一部分,它的最大优势是以最小的磁盘开销来侦测净行变更,它允许修改的数据以事务一致的形式表现,并提供了检测数据冲突的能力。它甚至可以根据外部传入的应用程序上下文,来完成更细颗粒度的更改处理,参看WITH CHANGE_TRACKING_CONTEXT (http://msdn.microsoft.com/zh-cn/library/bb895330%28v=sql.100%29.aspx)

复制代码
 /***使用“更改跟踪”以最小的磁盘开销跟踪净数据更改****/
/************* [email protected] 邀月 **************/

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

--启用更新跟踪,36小时清理一次
ALTER DATABASE TestDb4
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 36 HOURS,
AUTO_CLEANUP = ON)
复制代码

  注意,下一步是允许快照隔离,这是微软推祟的“最佳实践”,尽管这样行版本的生成会增加额外的空间使用,从而会增加总的I/O数量,但不使用快照会引发事务不一致的变更信息。

复制代码
ALTER DATABASE TestDb4
SET ALLOW_SNAPSHOT_ISOLATION ON
GO

SELECT DB_NAME(database_id) 数据库名称,is_auto_cleanup_on,
retention_period,retention_period_units_desc
FROM sys.change_tracking_databases
/*
数据库名称 is_auto_cleanup_on retention_period retention_period_units_desc
TestDb4 1 36 HOURS
*/

USE TestDb4
GO
--创建测试表
CREATE TABLE dbo.DepartDemo
([DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,
[DName] [nvarchar](200) NULL,
[Manager] [nvarchar](50) NULL,
[ParentID] [int] NOT NULL DEFAULT ((0)),
[CurState] [smallint] NOT NULL DEFAULT ((0)),
)
GO

----TRUNCATE table dbo.DepartDemo
----GO

--启用表的列更新跟踪
ALTER TABLE dbo.DepartDemo
ENABLE CHANGE_TRACKING
WITH (TRACK_COLUMNS_UPDATED = ON)

--确认是否更新跟踪开启
SELECT OBJECT_NAME(object_id) ObjNM,is_track_columns_updated_on
FROM sys.change_tracking_tables

/*
ObjNM is_track_columns_updated_on
DepartDemo 1
*/

--增加测试数据
INSERT dbo.DepartDemo
(DName,ParentID)
VALUES
('明教', 0),
('五行集', 101),
('少林派',0)

SELECT * FROM dbo.DepartDemo

--当前版本
SELECT CHANGE_TRACKING_CURRENT_VERSION ()
as 当前版本
/*
当前版本
1
*/
SELECT CHANGE_TRACKING_MIN_VALID_VERSION
( OBJECT_ID('dbo.DepartDemo') )as 最小可用版本

/*
最小可用版本
0
*/
复制代码

 

函数ChangeTable有两种用法来检测更改:
一、使用Changes关键字
二、使用Version关键字

复制代码
/*
一、使用Changes关键字
*/

SELECT DID,SYS_CHANGE_OPERATION,
SYS_CHANGE_VERSION
FROM CHANGETABLE
(CHANGES dbo.DepartDemo, 0) AS CT
复制代码

邀月工作室

复制代码
UPDATE dbo.DepartDemo
SET Manager='张无忌'
WHERE DID = 101

UPDATE dbo.DepartDemo
SET [DName] = '五行旗'
WHERE DID = 102

DELETE dbo.DepartDemo
WHERE DID = 103

SELECT CHANGE_TRACKING_CURRENT_VERSION () as 当前版本
/*
当前版本
4
*/

--版本1之后的更改
SELECT DID,
SYS_CHANGE_VERSION,
SYS_CHANGE_OPERATION,
SYS_CHANGE_COLUMNS
FROM CHANGETABLE
(CHANGES dbo.DepartDemo, 1) AS CT
复制代码

邀月工作室

复制代码
--返回哪些列被修改,1为真,0为假
SELECT DID,
CHANGE_TRACKING_IS_COLUMN_IN_MASK(
COLUMNPROPERTY(
OBJECT_ID('dbo.DepartDemo'),'DName', 'ColumnId') ,
SYS_CHANGE_COLUMNS) 是否改变DName,
CHANGE_TRACKING_IS_COLUMN_IN_MASK(
COLUMNPROPERTY(
OBJECT_ID('dbo.DepartDemo'), 'Manager', 'ColumnId') ,
SYS_CHANGE_COLUMNS) 是否改变Manager
FROM CHANGETABLE
(CHANGES dbo.DepartDemo, 1) AS CT
WHERE SYS_CHANGE_OPERATION = 'U'
/*
DID 是否改变DName 是否改变Manager
101 0 1
102 1 0
*/
复制代码

 

复制代码
/*
二、使用Version关键字
*/

SELECT d.DID, d.DName, d.Manager,
ct.SYS_CHANGE_VERSION
FROM dbo.DepartDemo d
CROSS APPLY CHANGETABLE
(VERSION dbo.DepartDemo , (DID), (d.DID)) as ct
复制代码


邀月工作室

复制代码
UPDATE dbo.DepartDemo
SET DName = '中原明教',
CurState = 0
WHERE DID = 101

SELECT d.DID, d.DName, d.Manager,
ct.SYS_CHANGE_VERSION
FROM dbo.DepartDemo d
CROSS APPLY CHANGETABLE
(VERSION dbo.DepartDemo , (DID), (d.DID)) as ct
复制代码

邀月工作室

复制代码
SELECT CHANGE_TRACKING_CURRENT_VERSION () as 当前版本
/*
当前版本
5
*/

--跟踪外部程序哪一部分引起的更改,这样好找出源头
DECLARE @context varbinary(128) = CAST('明教内讧引起分裂' as varbinary(128));
WITH CHANGE_TRACKING_CONTEXT (@context)
INSERT dbo.DepartDemo
(DName, Manager)
VALUES
('天鹰教', '殷天正')

--查询Context更改
SELECT DID,
SYS_CHANGE_OPERATION,
SYS_CHANGE_VERSION,
CAST(SYS_CHANGE_CONTEXT as varchar) ApplicationContext
FROM CHANGETABLE
(CHANGES dbo.DepartDemo, 5) AS CT
/*
DID SYS_CHANGE_OPERATION SYS_CHANGE_VERSION ApplicationContext
104 I 6 明教内讧引起分裂
*/
复制代码

 

 邀月工作室

 小结:

  本文总结了SQL Server中记录数据变更的四个方法:触发器、Output子句、SQL Server 2008中新增的变更数据捕获(CDC)功能、同步更改跟踪。其中后两个是SQL Server 2008中新增的功能,在SQL Server 2012中更是与Always ON紧密集成。

  1、不建议前两个。

  2、CDC用以实现异步跟踪用户表的数据修改,而且这一功能拥有最小的性能开销,可以用于其他数据源的持续更新,例如将OLTP数据库中的数据变更迁移到数据仓库数据库。

  3、”更改跟踪”的最大优势是以最小的磁盘开销来侦测净行变更,它允许修改的数据以事务一致的形式表现,并提供了检测数据冲突的能力。

 

其他推荐文章:

1、在VS中如何将数据同步配置为使用 SQL Server 更改跟踪

http://msdn.microsoft.com/zh-cn/library/cc714038.aspx

2、SQL Server 2012中复制、更改跟踪、更改数据捕获和 AlwaysOn 可用性组 (SQL Server)

http://msdn.microsoft.com/zh-cn/library/hh403414%28v=sql.110%29.aspx

3、在SSIS 2012中使用CDC(数据变更捕获)

http://www.cnblogs.com/downmoon/p/3700045.html

4、在SQL Server 2012中实现CDC for Oracle

http://www.cnblogs.com/downmoon/p/3702145.html

 

邀月注:本文版权由邀月和博客园共同所有,转载请注明出处。
助人等于自助!  [email protected]
分类: SQL Server 2008 Tips, 推荐
标签: 变更数据捕获, CDC, 更改跟踪, 触发器, Output子句
好文要顶 关注我 收藏该文 联系我
邀月
关注 - 23
粉丝 - 1494
荣誉: 推荐博客
+加关注
13
0
(请您对文章做出评价)
« 上一篇: 微软推出的免费新书《Introducing Microsoft SQL Server 2012》
» 下一篇: 在服务器上使用第三方独立组件对Word/Excel进行编程
posted @ 2012-04-10 04:18 邀月 阅读( 5154) 评论( 37) 编辑 收藏
  
#1楼 2012-04-10 06:04 CareySon  
好文,学习了。楼主睡得好晚或者说起的好早。。。
支持(0) 反对(0)
http://pic.cnblogs.com/face/35368/20140115190829.png
  
#2楼 2012-04-10 08:21 久依  
楼主 貌似 这个应该这样写吧 Captrue 应该是 Capture
支持(0) 反对(0)
http://pic.cnblogs.com/face/185422/20141016171216.png
  
#3楼 [ 楼主] 2012-04-10 08:33 邀月  
@CareySon
人生最大的痛苦莫过于有床有困意却睡不着觉。呵呵
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#4楼 [ 楼主] 2012-04-10 08:35 邀月  
@久依
Sorry,一次拼错,N次Copy会导致N个错误。thax!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#5楼 2012-04-10 09:19 Cheney Shue  
Oracle未来要放弃CDC和DataGuard了,按照微软有样学样的性子,这个功能估计也长不了
支持(0) 反对(0)
http://pic.cnblogs.com/face/u20714.jpg
  
#6楼 2012-04-10 09:20 yonghu86  
助人等于自助!
好文,此文经典!
支持(0) 反对(0)
http://pic.cnblogs.com/face/157572/20150613183043.png
  
#7楼 [ 楼主] 2012-04-10 09:42 邀月  
@Cheney Shue
未来会不会去掉可能出于多种原因,市场或性能、或其他,但CDC已经踏踏实实使用3年多了,2012中还得到了强化,感觉还是非常实用的,至少不用为每个业务对象再手工维护一张日志表了。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#8楼 [ 楼主] 2012-04-10 09:43 邀月  
@海南.胡勇
感谢支持!欢迎交流。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#9楼 2012-04-10 09:58 春色园  
留个爪,回来再学习
支持(0) 反对(0)
  
#10楼 [ 楼主] 2012-04-10 10:03 邀月  
@春色园
欢迎!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#11楼 2012-04-10 10:14 superboyli  
老大辛苦了!!!注意休息。快点出一个2012系列吧!!!
支持(0) 反对(0)
  
#12楼 2012-04-10 10:27 superboyli  
它允许修改的数据以事务一致的形式表现,并提供了检测数据冲突的能力。
这句话,怎么理解。老大!!!
支持(0) 反对(0)
  
#13楼 [ 楼主] 2012-04-10 11:21 邀月  
引用 superboyli:
它允许修改的数据以事务一致的形式表现,并提供了检测数据冲突的能力。
这句话,怎么理解。老大!!!
注意图中,
[img]https://public.sn2.livefilestore.com/y1pcJHRvLXtEkLMTnET0XFbLKlV_QZZNBm8X6qOAqeWBBZaUeUuG_u4j85EB4Xpcf4OJ6fD-Xv-4ATA75E8J7h1jw/2012-4-9%2019-20-58.png?psid=1[/img]
修改的行版本号由2变为5,而没修改过仍为3,也就是说,这里的版本号与最后提交的事务作用在表上的最新更改是匹配的。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#14楼 2012-04-10 11:26 superboyli  
@邀月

那检测数据冲突的能力,怎么理解!!!
支持(0) 反对(0)
  
#15楼 2012-04-10 11:36 Kingthy  
好文!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u25430.gif
  
#16楼 [ 楼主] 2012-04-10 12:05 邀月  
@superboyli
引用 superboyli:
@邀月
那检测数据冲突的能力,怎么理解!!!
这个匹配的过程就包含了行版本冲突检查。关于你提的这个问题,我后面会抽时间整理一篇文章来解释。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#17楼 [ 楼主] 2012-04-10 12:05 邀月  
@Kingthy
好就请按推荐,呵呵。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#18楼 2012-04-10 12:27 songcan  
请教楼主架构的变更是否有方法监视?
支持(0) 反对(0)
  
#19楼 [ 楼主] 2012-04-10 12:55 邀月  
引用 songcan:请教楼主架构的变更是否有方法监视?
CDC仅提供了对列架构的更改跟踪,看这里 http://msdn.microsoft.com/zh-cn/library/bb522553.aspx。
要想跟踪整个库或服务器级别的架构变化,据我所知,没有内置的函数。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#20楼 2012-04-10 13:38 hwnet  
@邀月
楼主,您好!我按照着您的步骤做示例,请问 SELECT sys.fn_cdc_map_time_to_lsn
( 'largest less than or equal' , '2012-04-09 23:59:59') as EndLSN ;为什么我查询出来的值是 NULL 呢?
支持(0) 反对(0)
  
#21楼 [ 楼主] 2012-04-10 15:14 邀月  
@hwnet
这是终止时间,今天是4月10日。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#22楼 2012-04-10 23:34 潇湘隐者  
好文章,学习了!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u73542.jpg
  
#23楼 2012-04-11 18:24 449268906  
挺不错的,您有时间我用户名是我qq,想请教您一些问题!
支持(0) 反对(0)
  
#24楼 2012-04-13 09:25 hwnet  
引用 邀月:
@hwnet
这是终止时间,今天是4月10日。

SELECT sys.fn_cdc_map_time_to_lsn
( 'smallest greater than or equal' , '2012-04-09 09:09:30') as BeginLSN

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

查询BeginLSN=0x0000001B000000AE007A , EndLSN=NULL ;请问这里的 终止时间 不能用一天之内的吗?
支持(0) 反对(0)
  
#25楼 [ 楼主] 2012-04-13 10:19 邀月  
引用 hwnet:
查询BeginLSN=0x0000001B000000AE007A , EndLSN=NULL ;请问这里的 终止时间 不能用一天之内的吗?

不是不能用,而是
sys.fn_cdc_map_time_to_lsn( 'largest less than or equal' , '2012-04-09 23:59:59')
确定在 cdc.lsn_time_mapping系统表中是否有 tran_end_time 值小于或等于"2012-04-09 23:59:59"的行,有,返回一个LSN序列号。如果没有,返回Null。我的意思是你的表中有“ 2012-04-09 16:09:30”之后,但却没有“ 2012-04-09 23:59:59”之前的更改。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#26楼 2012-04-13 10:47 hwnet  
@邀月
哦,这样啊!多谢多谢!我还有一个疑问:当BeginLSN=0x0000001B000000AE007A , EndLSN=NULL 时;查询FROM [cdc].[fn_cdc_get_all_changes_dbo_DepartDemo]
(@FromLSN, @ToLSN, 'all update old') 这个变更记录的时候,会提示报错啊!故障在这个条件 and (t.__$start_lsn <= @to_lsn) and (t.__$start_lsn >= @from_lsn) ???

请问这种情况应该怎么处理要好一些呢?您就给我一些好的建议吧!再次深表感谢啊!呵呵。
支持(0) 反对(0)
  
#27楼 [ 楼主] 2012-04-13 12:06 邀月  
@hwnet
既然Null,那就是没有LSN边界了,这种情况在手工指定时应该不会出现,如果在Job中,可以用文中提供的最大最小函数来确保查询有效,如:
?
1
2
--获取可用的最大边界
SELECT sys.fn_cdc_get_max_lsn () Max_LSN
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#28楼 2012-04-13 15:50 hwnet  
@邀月
哦,我知道了!谢谢。
支持(0) 反对(0)
  
#29楼 2012-04-14 10:37 深蓝医生  
先顶再看!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u114517.png?id=23112533
  
#30楼 [ 楼主] 2012-04-14 10:50 邀月  
引用 深蓝医生:先顶再看!
灰常感谢支持!
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#31楼 2012-05-28 11:36 黑头  
实用! 谢谢分享
支持(0) 反对(0)
  
#32楼 [ 楼主] 2012-05-29 22:19 邀月  
@黑头
感谢关注,欢迎交流
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#33楼 2012-09-23 22:40 陈谨  
楼主,如果我想知道是谁做了这些数据变更操作,该怎么做呢?
支持(0) 反对(0)
  
#34楼 [ 楼主] 2012-09-24 21:42 邀月  
@陈谨
最好用第三方独立的日志来记录操作人的痕迹。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#35楼 2013-08-26 11:42 上山打老虎  
请问博主有没有不依赖数据库功能做操作日志功能的解决方案?
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18730.jpg
  
#36楼 [ 楼主] 2013-08-29 16:49 邀月  
@上山打老虎
有,结合JBPM和log4J做过。
支持(0) 反对(0)
http://pic.cnblogs.com/face/u18486.jpg
  
#37楼 2771343 2013/9/9 11:09:38 2013-09-09 11:09 上山打老虎  
@邀月
引用 @上山打老虎
有,结合JBPM和log4J做过。
.net下有相应的吗?

你可能感兴趣的:(数据库)