Vì vậy, sau một số nghiên cứu, chúng tôi quyết định vẫn làm điều này ở phía SQL trước khi chuyển giao cho kho dữ liệu. Nhưng chúng tôi đang thực hiện phương pháp cải tiến này (dựa trên nhu cầu của chúng tôi và sự hiểu biết mới về cách thức hoạt động của mặt nạ).
Chúng tôi nhận được một danh sách các tên cột và vị trí thứ tự của chúng với truy vấn này. Sự trở lại trở lại trong một định dạng XML để chúng ta có thể chuyển sang SQL CLR.
DECLARE @colListXML varchar(max);
SET @colListXML = (SELECT column_name, column_ordinal
FROM cdc.captured_columns
INNER JOIN cdc.change_tables
ON captured_columns.[object_id] = change_tables.[object_id]
WHERE capture_instance = 'dbo_OurTableName'
FOR XML Auto);
Sau đó, chúng tôi chuyển khối XML đó dưới dạng một biến và trường mặt nạ cho hàm CLR trả về chuỗi được phân cách bằng dấu phẩy của các cột đã thay đổi theo trường nhị phân _ $ update_mask. Hàm clr này thẩm vấn trường mặt nạ để thay đổi bit cho từng cột trong danh sách xml và sau đó trả về tên của nó từ thứ tự liên quan.
SELECT cdc.udf_clr_ChangedColumns(@colListXML,
CAST(__$update_mask AS VARCHAR(MAX))) AS changed
FROM cdc.dbo_OurCaptureTableName
WHERE NOT __$update_mask IS NULL;
Mã c # clr trông như thế này: (được biên dịch thành một hội đồng gọi là CDCUtilities)
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString udf_clr_cdcChangedColumns(string columnListXML, string updateMaskString)
{
/* xml of column ordinals shall be formatted as follows:
<cdc.captured_columns column_name="Column1" column_ordinal="1" />
<cdc.captured_columns column_name="Column2" column_ordinal="2" />
*/
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
byte[] updateMask = encoding.GetBytes(updateMaskString);
string columnList = "";
System.Xml.XmlDocument colList = new System.Xml.XmlDocument();
colList.LoadXml("<columns>" + columnListXML + "</columns>"); /* generate xml with root node */
for (int i = 0; i < colList["columns"].ChildNodes.Count; i++)
{
if (columnChanged(updateMask, int.Parse(colList["columns"].ChildNodes[i].Attributes["column_ordinal"].Value)))
{
columnList += colList["columns"].ChildNodes[i].Attributes["column_name"].Value + ",";
}
}
if (columnList.LastIndexOf(',') > 0)
{
columnList = columnList.Remove(columnList.LastIndexOf(',')); /* get rid of trailing comma */
}
return columnList; /* return the comma seperated list of columns that changed */
}
private static bool columnChanged(byte[] updateMask, int colOrdinal)
{
unchecked
{
byte relevantByte = updateMask[(updateMask.Length - 1) - ((colOrdinal - 1) / 8)];
int bitMask = 1 << ((colOrdinal - 1) % 8);
var hasChanged = (relevantByte & bitMask) != 0;
return hasChanged;
}
}
}
Và chức năng cho CLR như thế này:
CREATE FUNCTION [cdc].[udf_clr_ChangedColumns]
(@columnListXML [nvarchar](max), @updateMask [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [CDCUtilities].[UserDefinedFunctions].[udf_clr_cdcChangedColumns]
Sau đó chúng tôi sẽ thêm danh sách cột này vào hàng và chuyển đến kho dữ liệu để phân tích. Bằng cách sử dụng truy vấn và clr, chúng tôi tránh phải sử dụng hai lệnh gọi hàm trên mỗi hàng cho mỗi thay đổi. Chúng ta có thể bỏ qua quyền thịt với kết quả được tùy chỉnh cho trường hợp chụp thay đổi của chúng ta.
Nhờ bài đăng stackoverflow này được đề xuất bởi Jon Seigel cho cách diễn giải mặt nạ.
Theo kinh nghiệm của chúng tôi với phương pháp này, chúng tôi có thể nhận được danh sách tất cả các cột đã thay đổi từ 10k cdc hàng trong vòng dưới 3 giây.