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

原博客地址:http://blog.csdn.net/downmoon/article/details/7443627

SQL Server 2008中SQL应用系列--目录索引

  本文主要介绍SQL Server中记录数据变更的四个方法:触发器、Output子句、SQL Server 2008中新增的变更数据捕获(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语句在某些列不明确的情况下报错)。我们看两个例子:

  准备基础数据:

  1. USE testDb2  
  2. GO  
  3. --创建两个测试表  
  4. IF NOT OBJECT_ID('DepartDemo'IS NULL  
  5. DROP TABLE [DepartDemo]  
  6. GO  
  7. IF NOT OBJECT_ID('DepartChangeLogs'IS NULL  
  8. DROP TABLE [DepartChangeLogs]  
  9. GO  
  10. --测试表  
  11. CREATE TABLE [dbo].[DepartDemo](  
  12. [DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,  
  13. [DName] [nvarchar](200) NULL,  
  14. [DCode] [nvarchar](500) NULL,  
  15. [Manager] [nvarchar](50) NULL,  
  16. [ParentID] [intNOT NULL DEFAULT ((0)),  
  17. [AddUser] [nvarchar](50) NULL,  
  18. [AddTime] [datetime] NULL,  
  19. [ModUser] [nvarchar](50) NULL,  
  20. [ModTime] [datetime] NULL,  
  21. [CurState] [smallintNOT NULL DEFAULT ((0)),  
  22. [Remark] [nvarchar](500) NULL,  
  23. [F1] [intNOT NULL DEFAULT ((0)),  
  24. [F2] [nvarchar](300) NULL  
  25. )  
  26. GO  
  27. --记录日志表  
  28. CREATE TABLE [DepartChangeLogs]  
  29. ([LogID] [bigint] IDENTITY(1001,1) NOT NULL PRIMARY KEY,  
  30. [DID] [intNOT NULL,  
  31. [DName] [nvarchar](200) NULL,  
  32. [DCode] [nvarchar](500) NULL,  
  33. [Manager] [nvarchar](50) NULL,  
  34. [ParentID] [intNOT NULL DEFAULT ((0)),  
  35. [AddUser] [nvarchar](50) NULL,  
  36. [AddTime] [datetime] NULL,  
  37. [ModUser] [nvarchar](50) NULL,  
  38. [ModTime] [datetime] NULL,  
  39. [CurState] [smallintNOT NULL DEFAULT ((0)),  
  40. [Remark] [nvarchar](500) NULL,  
  41. [F1] [intNOT NULL DEFAULT ((0)),  
  42. [F2] [nvarchar](300) NULL,  
  43. [LogTime] DateTime Default(Getdate()) Not Null,  
  44. [InsOrUpd] char not null  
  45. )  
  46. GO  

创建触发器:

  1. /******* 创建一个After DML触发器 ******/  
  2. /********* 3w@live.cn 邀月***************/  
  3. CREATE TRIGGER dbo.tri_LogDepartDemo  
  4. ON [dbo].[DepartDemo]  
  5. AFTER INSERTDelete /************此处使用update与“Insert,Delete”效果是一样的,邀月注 **********/  
  6. AS  
  7. SET NOCOUNT ON --屏蔽触发器发送“受影响的行数”给应用程序  
  8. -- Inserted rows  
  9. INSERT [DepartChangeLogs]  
  10. (DID,[DName], [DCode], [Manager], [ParentID],  
  11. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],  
  12. LogTime, InsOrUPD)  
  13. SELECT DISTINCT DID,[DName], [DCode], [Manager], [ParentID],  
  14. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],  
  15. GETDATE(), 'I'  
  16. FROM inserted i  
  17. -- Deleted rows  
  18. INSERT [DepartChangeLogs]  
  19. (DID,[DName], [DCode], [Manager], [ParentID],  
  20. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],  
  21. LogTime, InsOrUPD)  
  22. SELECT DISTINCT DID,[DName], [DCode], [Manager], [ParentID],  
  23. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2],  
  24. GETDATE(), 'D'  
  25. FROM deleted d  
  26. GO  
  27. INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],  
  28. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])  
  29. VALUES (N'国家统计局房产审计一科', N'0', N'胡不归', 0, N'DeomUser',  
  30. CAST(0x00009DF7017B6F96 AS DateTime), N''CAST(0x0000000000000000 AS DateTime),  
  31. 1, N'专业评估全国房价,为老百姓谋福祉', 0, N'')  
  32. GO  
  33. ----该Update不会被触发器记录,但Update会生效  
  34. UPDATE departDemo SET [Manager]='任我行' WHERE DID=101  
  35. GO  
  36. DELETE FROM departDemo where DID=101  
  37. GO  
  38. SELECT * FROM [DepartChangeLogs]  

统计效果: 
邀月工作室 
如果你觉得触发器过于浪费,你可以试着根据某些字段以缩小触发器的范围 

  1. /********* 使用DML触发器记录特定列的修改 ***/  
  2. /********* 3w@live.cn 邀月***************/  
  3. CREATE TRIGGER dbo.[tri_LogDepartDemo2]  
  4. ON [dbo].[DepartDemo]  
  5. AFTER Update  
  6. AS  
  7. IF Update([Manager])  
  8. Begin  
  9. print '该部门主管实行终身任免制,不得中途更改!'  
  10. Rollback ----回滚Update操作  
  11. End  
  12. GO  
  13. UPDATE departDemo SET [Manager]='任我行' WHERE DID=101  
  14. 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 子句的结果,然后将这些结果插入目标表或视图。

  举例:

  1. /********* 使用Output记录表记录的修改 *****/  
  2. /********* 3w@live.cn 邀月***************/  
  3. ----删除前面的触发器  
  4. Drop TRIGGER dbo.[tri_LogDepartDemo]  
  5. DROP TRIGGER dbo.[tri_LogDepartDemo2]  
  6. INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],  
  7. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])  
  8. OUTPUT Inserted.*,getdate(),'I' ---注意这行是新增的  
  9. INTO DepartChangeLogs ---注意这行是新增的  
  10. VALUES (N'发改委', N'0', N'向问天', 0, N'DeomUser',  
  11. CAST(0x00009DF7017B6F96 AS DateTime), N''CAST(0x0000000000000000 AS DateTime),  
  12. 1, N'油价,我说了算', 0, N'')  
  13. GO  
  14. 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

  1. /**************异步跟踪数据更新演示*************/  
  2. /************* 3w@live.cn 邀月***************/  
  3. use master  
  4. GO  
  5.   
  6. IF EXISTS (SELECT [nameFROM sys.databases WHERE name = 'TestDb2')  
  7. drop DATABASE TestDb2  
  8. Go  
  9. CREATE DATABASE TestDb2  
  10. GO  
  11.   
  12. --查看是否启用CDC  
  13. SELECT is_cdc_enabled FROM sys.databases WHERE name = 'TestDb2'  
  14.   
  15. USE TestDb2  
  16. GO  
  17. ----启用当前数据库的CDC功能  
  18. EXEC sys.sp_cdc_enable_db  
  19. GO  
  20.   
  21. /**************  
  22. 如果报15517错误,请换用其他owner,邀月注  
  23.   
  24. ******/  
  25.   
  26. SELECT is_cdc_enabled FROM sys.databases WHERE name = 'TestDb2'  
  27.   
  28. /*  
  29. is_cdc_enabled  
  30. 1  
  31. */  
  32. USE testDb2  
  33. GO  
  34.   
  35. CREATE TABLE [dbo].[DepartDemo](  
  36. [DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,  
  37. [DName] [nvarchar](200) NULL,  
  38. [DCode] [nvarchar](500) NULL,  
  39. [Manager] [nvarchar](50) NULL,  
  40. [ParentID] [intNOT NULL DEFAULT ((0)),  
  41. [AddUser] [nvarchar](50) NULL,  
  42. [AddTime] [datetime] NULL,  
  43. [ModUser] [nvarchar](50) NULL,  
  44. [ModTime] [datetime] NULL,  
  45. [CurState] [smallintNOT NULL DEFAULT ((0)),  
  46. [Remark] [nvarchar](500) NULL,  
  47. [F1] [intNOT NULL DEFAULT ((0)),  
  48. [F2] [nvarchar](300) NULL  
  49. )  
  50. GO  
  51.   
  52. /**********************************  
  53. 需要启用SQL Server Agent服务,否则会报错,邀月注  
  54. SQLServerAgent is not currently running so it cannot be notified of this action.  
  55. ***********************************/  
  56.   
  57. /****** 捕获所有的行变更,只返回行的净变更,其他默认 *******/  
  58. EXEC sys.sp_cdc_enable_table  
  59. @source_schema = 'dbo',  
  60. @source_name = 'DepartDemo',  
  61. @role_name = NULL,  
  62. @capture_instance = NULL,  
  63. @supports_net_changes = 1,  
  64. @index_name = NULL,  
  65. @captured_column_list = NULL,  
  66. @filegroup_name = default  

  注意此时,SQL Server 自启动了两个job,一个捕获,一个清除,注意清除是默认凌晨2点,清除72小时以上的数据。如果同一数据库的表中CDC已经启用,不会重建job。

  1. /*  
  2. Job 'cdc.TestDb2_capture' started successfully.  
  3. Job 'cdc.TestDb2_cleanup' started successfully.  
  4. */  
  5.   
  6. --确认表已经被跟踪  
  7.   
  8. SELECT is_tracked_by_cdc FROM sys.tables  
  9. WHERE name = 'DepartDemo' and schema_id = SCHEMA_ID('dbo')  
  10. /*  
  11. is_tracked_by_cdc  
  12. 1  
  13. */  
  14.   
  15. --确认  
  16. EXEC sys.sp_cdc_help_change_data_capture 'dbo''DepartDemo'  

邀月工作室

增加了一个表[cdc].[dbo_DepartDemo_CT] 
相比源表多了个字段: 
[__$start_lsn] 
,[__$end_lsn] 
,[__$seqval] 
,[__$operation] 
,[__$update_mask] 

邀月工作室

不建议直接查询该表,而应该使用下面的技巧

  1. USE TestDb2  
  2. GO  
  3.   
  4. INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],  
  5. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])  
  6. VALUES (N'银监会', N'0', N'云中鹤', 0, N'DemoUser1',  
  7. CAST(0x00009DF7017B6F96 AS DateTime), N''CAST(0x0000000000000000 AS DateTime),  
  8. 1, N'监管汇率', 0, N'')  
  9.   
  10. INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],  
  11. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])  
  12. VALUES (N'统计局', N'0', N'神算子', 0, N'DemoUser2',  
  13. CAST(0x00009DF7017B6F96 AS DateTime), N''CAST(0x0000000000000000 AS DateTime),  
  14. 1, N'统计数据', 0, N'')  
  15. GO  
  16.   
  17. UPDATE [dbo].[DepartDemo]  
  18. SET Manager='段正淳'  
  19. WHERE DID =101  
  20.   
  21. DELETE [dbo].[DepartDemo]  
  22. 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转换为时间。

  1. /******* 使用LSN 查看CDC记录 *********/  
  2.   
  3. --http://msdn.microsoft.com/zh-cn/library/bb500137%28v=sql.100%29.aspx  
  4. SELECT sys.fn_cdc_map_time_to_lsn  
  5. 'smallest greater than or equal' , '2012-04-09 16:09:30'as BeginLSN  
  6.   
  7. /*  
  8. BeginLSN  
  9. 0x0000002C000000AA0003  
  10. */  
  11.   
  12. SELECT sys.fn_cdc_map_time_to_lsn  
  13. 'largest less than or equal' , '2012-04-09 23:59:59'as EndLSN  
  14.   
  15. /*  
  16. EndLSN  
  17. 0x0000002C000001C20005  
  18. */  
  19.   
  20. /**************查看所有CDC记录*************/  
  21. /************* 3w@live.cn 邀月***************/  
  22.   
  23. DECLARE @FromLSN varbinary(10) =  
  24. sys.fn_cdc_map_time_to_lsn  
  25. 'smallest greater than or equal' , '2012-04-09 16:09:30')  
  26.   
  27. DECLARE @ToLSN varbinary(10) =  
  28. sys.fn_cdc_map_time_to_lsn  
  29. 'largest less than or equal' , '2012-04-09 23:59:59')  
  30.   
  31. SELECT  
  32. __$operation,  
  33. __$update_mask,  
  34. DID,  
  35. DName,  
  36. Manager  
  37. FROM [cdc].[fn_cdc_get_all_changes_dbo_DepartDemo]  
  38. (@FromLSN, @ToLSN, 'all')  
  39.   
  40. /************查看所有更新*************************  
  41.   
  42. __$operation __$update_mask DID DName Manager  
  43. 2 0x1FFF 105 银监会 云中鹤  
  44. 2 0x1FFF 106 统计局 神算子  
  45. 1 0x1FFF 101 银监会 段正淳  
  46. 1 0x1FFF 103 银监会 云中鹤  
  47. 1 0x1FFF 104 统计局 神算子  
  48. 1 0x1FFF 105 银监会 云中鹤  
  49. 1 0x1FFF 106 统计局 神算子  
  50. 2 0x1FFF 107 银监会 云中鹤  
  51. 2 0x1FFF 108 统计局 神算子  
  52. 4 0x0008 107 银监会 段正淳  
  53. 1 0x1FFF 108 统计局 神算子  
  54. */  
  55.   
  56. /**************查看所有CDC记录*************/  
  57. /************* 3w@live.cn 邀月***************/  
  58. DECLARE @FromLSN varbinary(10) =  
  59. sys.fn_cdc_map_time_to_lsn  
  60. 'smallest greater than or equal' , '2012-04-09 16:09:30')  
  61.   
  62. DECLARE @ToLSN varbinary(10) =  
  63. sys.fn_cdc_map_time_to_lsn  
  64. 'largest less than or equal' , '2012-04-09 23:59:59')  
  65.   
  66. --解释一下Operation的具体含义  
  67. SELECT  
  68. CASE __$operation  
  69. WHEN 1 THEN 'DELETE'  
  70. WHEN 2 THEN 'INSERT'  
  71. WHEN 3 THEN 'Before UPDATE'  
  72. WHEN 4 THEN 'After UPDATE'  
  73. END Operation,  
  74. __$update_mask,  
  75. DID,  
  76. DName,  
  77. Manager  
  78. FROM [cdc].[fn_cdc_get_all_changes_dbo_DepartDemo]  
  79. (@FromLSN, @ToLSN, 'all update old')  

邀月工作室

  1. /**************查看净更改(Net changes)CDC记录*************/  
  2. /************* 3w@live.cn 邀月 ***************/  
  3.   
  4. INSERT [dbo].[DepartDemo] ([DName], [DCode], [Manager], [ParentID],  
  5. [AddUser], [AddTime], [ModUser], [ModTime], [CurState], [Remark], [F1], [F2])  
  6. VALUES (N'药监局', N'0', N'蝶谷医仙', 0, N'DemoUser3',  
  7. CAST(0x00009DF7017B6F96 AS DateTime), N''CAST(0x0000000000000000 AS DateTime),  
  8. 1, N'制定药价', 0, N'')  
  9. GO  
  10.   
  11. UPDATE [dbo].[DepartDemo]  
  12. SET Manager='胡青牛'  
  13. WHERE DID =109  
  14.   
  15. DECLARE @FromLSN varbinary(10) =  
  16. sys.fn_cdc_map_time_to_lsn  
  17. 'smallest greater than or equal' , '2012-04-09 16:09:30')  
  18.   
  19. DECLARE @ToLSN varbinary(10) =  
  20. sys.fn_cdc_map_time_to_lsn  
  21. 'largest less than or equal' , '2012-04-09 23:59:59')  
  22.   
  23. SELECT  
  24. CASE __$operation  
  25. WHEN 1 THEN 'DELETE'  
  26. WHEN 2 THEN 'INSERT'  
  27. WHEN 3 THEN 'Before UPDATE'  
  28. WHEN 4 THEN 'After UPDATE'  
  29. WHEN 5 THEN 'MERGE'  
  30. END Operation,  
  31. __$update_mask,  
  32. DID,  
  33. DName,  
  34. Manager  
  35. FROM [cdc].[fn_cdc_get_net_changes_dbo_DepartDemo]  
  36. (@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

  1. /************** 转换CDC更新掩码 *************/  
  2. /************* 3w@live.cn 邀月 **************/  
  3.   
  4. UPDATE dbo.[DepartDemo]  
  5. SET [Manager] = '东方不败'  
  6. WHERE DID =107  
  7.   
  8. UPDATE dbo.[DepartDemo]  
  9. SET ParentID = 109  
  10. WHERE DID =107  
  11.   
  12. DECLARE @FromLSN varbinary(10) =  
  13. sys.fn_cdc_map_time_to_lsn  
  14. 'smallest greater than or equal' , '2012-04-09 16:09:30')  
  15.   
  16. DECLARE @ToLSN varbinary(10) =  
  17. sys.fn_cdc_map_time_to_lsn  
  18. 'largest less than or equal' , '2012-04-09 23:59:59')  
  19.   
  20. SELECT  
  21. sys.fn_cdc_is_bit_set (  
  22. sys.fn_cdc_get_column_ordinal (  
  23. 'dbo_DepartDemo' , 'Manager' ),  
  24. __$update_mask) Manager_Updated,  
  25. sys.fn_cdc_is_bit_set (  
  26. sys.fn_cdc_get_column_ordinal (  
  27. 'dbo_DepartDemo' , 'ParentID' ),  
  28. __$update_mask) ParentID_Updated,  
  29. DID,  
  30. Manager,  
  31. ParentID  
  32. FROM cdc.fn_cdc_get_all_changes_dbo_DepartDemo  
  33. (@FromLSN, @ToLSN, 'all')  
  34. 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

  示例如下:

  1. /************** 获取LSN边界的其他方法 *************/  
  2. /************* 3w@live.cn 邀月 **************/  
  3.   
  4. --获取最小边界  
  5. SELECT sys.fn_cdc_get_min_lsn ('dbo_DepartDemo') Min_LSN  
  6. --获取可用的最大边界  
  7. SELECT sys.fn_cdc_get_max_lsn () Max_LSN  
  8. --获取最大边界的下一个序号  
  9. SELECT sys.fn_cdc_increment_lsn (sys.fn_cdc_get_max_lsn()) New_Lower_Bound_LSN  
  10. --获取最大边界的前一个序号  
  11. SELECT sys.fn_cdc_decrement_lsn (sys.fn_cdc_get_max_lsn())  
  12. 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代理作业。

  1. /************** 在数据库和表级禁用CDC *************/  
  2. /************* 3w@live.cn 邀月 **************/  
  3.   
  4. EXEC sys.sp_cdc_disable_table 'dbo''DepartDemo''all'  
  5.   
  6. SELECT is_tracked_by_cdc FROM sys.tables  
  7. WHERE name = 'DepartDemo' and schema_id = SCHEMA_ID('dbo')  
  8.   
  9. --当前数据库上禁用CDC  
  10. 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

  1. /***使用“更改跟踪”以最小的磁盘开销跟踪净数据更改****/  
  2. /************* 3w@live.cn 邀月 **************/  
  3.   
  4. IF EXISTS (SELECT [nameFROM sys.databases WHERE name = 'TestDb4')  
  5. drop DATABASE TestDb4  
  6. Go  
  7. CREATE DATABASE TestDb4  
  8. GO  
  9.   
  10. --启用更新跟踪,36小时清理一次  
  11. ALTER DATABASE TestDb4  
  12. SET CHANGE_TRACKING = ON  
  13. (CHANGE_RETENTION = 36 HOURS,  
  14. AUTO_CLEANUP = ON)  

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

  1. ALTER DATABASE TestDb4  
  2. SET ALLOW_SNAPSHOT_ISOLATION ON  
  3. GO  
  4.   
  5. SELECT DB_NAME(database_id) 数据库名称,is_auto_cleanup_on,  
  6. retention_period,retention_period_units_desc  
  7. FROM sys.change_tracking_databases  
  8. /*  
  9. 数据库名称 is_auto_cleanup_on retention_period retention_period_units_desc  
  10. TestDb4 1 36 HOURS  
  11. */  
  12.   
  13. USE TestDb4  
  14. GO  
  15. --创建测试表  
  16. CREATE TABLE dbo.DepartDemo  
  17. ([DID] [int] IDENTITY(101,1) NOT NULL PRIMARY KEY,  
  18. [DName] [nvarchar](200) NULL,  
  19. [Manager] [nvarchar](50) NULL,  
  20. [ParentID] [intNOT NULL DEFAULT ((0)),  
  21. [CurState] [smallintNOT NULL DEFAULT ((0)),  
  22. )  
  23. GO  
  24.   
  25. ----TRUNCATE table dbo.DepartDemo  
  26. ----GO  
  27.   
  28. --启用表的列更新跟踪  
  29. ALTER TABLE dbo.DepartDemo  
  30. ENABLE CHANGE_TRACKING  
  31. WITH (TRACK_COLUMNS_UPDATED = ON)  
  32.   
  33. --确认是否更新跟踪开启  
  34. SELECT OBJECT_NAME(object_id) ObjNM,is_track_columns_updated_on  
  35. FROM sys.change_tracking_tables  
  36.   
  37. /*  
  38. ObjNM is_track_columns_updated_on  
  39. DepartDemo 1  
  40. */  
  41.   
  42. --增加测试数据  
  43. INSERT dbo.DepartDemo  
  44. (DName,ParentID)  
  45. VALUES  
  46. ('明教', 0),  
  47. ('五行集', 101),  
  48. ('少林派',0)  
  49.   
  50. SELECT * FROM dbo.DepartDemo  
  51.   
  52. --当前版本  
  53. SELECT CHANGE_TRACKING_CURRENT_VERSION ()  
  54. as 当前版本  
  55. /*  
  56. 当前版本  
  57. 1  
  58. */  
  59. SELECT CHANGE_TRACKING_MIN_VALID_VERSION  
  60. ( OBJECT_ID('dbo.DepartDemo') )as 最小可用版本  
  61.   
  62. /*  
  63. 最小可用版本  
  64. 0  
  65. */  

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

  1. /*  
  2. 一、使用Changes关键字  
  3. */  
  4.   
  5. SELECT DID,SYS_CHANGE_OPERATION,  
  6. SYS_CHANGE_VERSION  
  7. FROM CHANGETABLE  
  8. (CHANGES dbo.DepartDemo, 0) AS CT  


邀月工作室

  1. UPDATE dbo.DepartDemo  
  2. SET Manager='张无忌'  
  3. WHERE DID = 101  
  4.   
  5. UPDATE dbo.DepartDemo  
  6. SET [DName] = '五行旗'  
  7. WHERE DID = 102  
  8.   
  9. DELETE dbo.DepartDemo  
  10. WHERE DID = 103  
  11.   
  12. SELECT CHANGE_TRACKING_CURRENT_VERSION () as 当前版本  
  13. /*  
  14. 当前版本  
  15. 4  
  16. */  
  17.   
  18. --版本1之后的更改  
  19. SELECT DID,  
  20. SYS_CHANGE_VERSION,  
  21. SYS_CHANGE_OPERATION,  
  22. SYS_CHANGE_COLUMNS  
  23. FROM CHANGETABLE  
  24. (CHANGES dbo.DepartDemo, 1) AS CT   

邀月工作室

  1. --返回哪些列被修改,1为真,0为假  
  2. SELECT DID,  
  3. CHANGE_TRACKING_IS_COLUMN_IN_MASK(  
  4. COLUMNPROPERTY(  
  5. OBJECT_ID('dbo.DepartDemo'),'DName''ColumnId') ,  
  6. SYS_CHANGE_COLUMNS) 是否改变DName,  
  7. CHANGE_TRACKING_IS_COLUMN_IN_MASK(  
  8. COLUMNPROPERTY(  
  9. OBJECT_ID('dbo.DepartDemo'), 'Manager''ColumnId') ,  
  10. SYS_CHANGE_COLUMNS) 是否改变Manager  
  11. FROM CHANGETABLE  
  12. (CHANGES dbo.DepartDemo, 1) AS CT  
  13. WHERE SYS_CHANGE_OPERATION = 'U'  
  14. /*  
  15. DID 是否改变DName 是否改变Manager  
  16. 101 0 1  
  17. 102 1 0  
  18. */  
  1. /*  
  2. 二、使用Version关键字  
  3. */  
  4.   
  5. SELECT d.DID, d.DName, d.Manager,  
  6. ct.SYS_CHANGE_VERSION  
  7. FROM dbo.DepartDemo d  
  8. CROSS APPLY CHANGETABLE  
  9. (VERSION dbo.DepartDemo , (DID), (d.DID)) as ct   


邀月工作室

  1. UPDATE dbo.DepartDemo  
  2. SET DName = '中原明教',  
  3. CurState = 0  
  4. WHERE DID = 101  
  5.   
  6. SELECT d.DID, d.DName, d.Manager,  
  7. ct.SYS_CHANGE_VERSION  
  8. FROM dbo.DepartDemo d  
  9. CROSS APPLY CHANGETABLE  
  10. (VERSION dbo.DepartDemo , (DID), (d.DID)) as ct   

 

邀月工作室

  1. SELECT CHANGE_TRACKING_CURRENT_VERSION () as 当前版本  
  2. /*  
  3. 当前版本  
  4. 5  
  5. */  
  6.   
  7. --跟踪外部程序哪一部分引起的更改,这样好找出源头  
  8. DECLARE @context varbinary(128) = CAST('明教内讧引起分裂' as varbinary(128));  
  9. WITH CHANGE_TRACKING_CONTEXT (@context)  
  10. INSERT dbo.DepartDemo  
  11. (DName, Manager)  
  12. VALUES  
  13. ('天鹰教''殷天正')  
  14.   
  15. --查询Context更改  
  16. SELECT DID,  
  17. SYS_CHANGE_OPERATION,  
  18. SYS_CHANGE_VERSION,  
  19. CAST(SYS_CHANGE_CONTEXT as varchar) ApplicationContext  
  20. FROM CHANGETABLE  
  21. (CHANGES dbo.DepartDemo, 5) AS CT  
  22. /*  
  23. DID SYS_CHANGE_OPERATION SYS_CHANGE_VERSION ApplicationContext  
  24. 104 I 6 明教内讧引起分裂  
  25. */  

 

邀月工作室

小结:

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

  1、不建议前两个。

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

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

 

posted @ 2012-04-13 16:11  范尼  阅读(271)  评论(0)    收藏  举报