ดังนั้นหลังจากการวิจัยบางอย่างเราตัดสินใจที่จะยังคงทำสิ่งนี้ในฝั่ง SQL ก่อนที่จะส่งไปยังคลังข้อมูล แต่เรากำลังปรับปรุงวิธีการนี้ให้ดีขึ้นมาก (ขึ้นอยู่กับความต้องการของเราและความเข้าใจใหม่เกี่ยวกับการทำงานของหน้ากาก)
เราได้รับรายชื่อคอลัมน์และตำแหน่งอันดับของพวกเขาด้วยแบบสอบถามนี้ การส่งคืนจะกลับมาในรูปแบบ XML เพื่อให้เราสามารถส่งผ่านไปยัง 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);
จากนั้นเราจะส่งผ่านบล็อก XML นั้นเป็นตัวแปรและฟิลด์หน้ากากไปยังฟังก์ชัน CLR ที่ส่งคืนสตริงที่คั่นด้วยเครื่องหมายจุลภาคของคอลัมน์ที่เปลี่ยนไปตามฟิลด์ไบนารี _ $ update_mask ฟังก์ชัน clr นี้สอบถามเขตข้อมูลรูปแบบการเปลี่ยนแปลงของบิตสำหรับแต่ละคอลัมน์ในรายการ xml จากนั้นส่งคืนชื่อของมันจากลำดับที่เกี่ยวข้อง
SELECT cdc.udf_clr_ChangedColumns(@colListXML,
CAST(__$update_mask AS VARCHAR(MAX))) AS changed
FROM cdc.dbo_OurCaptureTableName
WHERE NOT __$update_mask IS NULL;
รหัส c # clr มีลักษณะดังนี้: (รวบรวมเป็นชุดประกอบที่เรียกว่า 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;
}
}
}
และฟังก์ชั่นของ CLR ดังนี้:
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]
จากนั้นเราจะเพิ่มรายการคอลัมน์นี้ต่อท้ายชุดข้อมูลและส่งต่อไปยังคลังข้อมูลเพื่อการวิเคราะห์ โดยการใช้แบบสอบถามและ clr เราหลีกเลี่ยงการใช้สองการเรียกใช้ฟังก์ชั่นต่อแถวต่อการเปลี่ยนแปลง เราสามารถข้ามไปที่เนื้อด้วยผลลัพธ์ที่กำหนดเองสำหรับอินสแตนซ์การจับการเปลี่ยนแปลงของเรา
ขอบคุณโพสต์สแต็คโอเวอร์โฟลว์ที่ Jon Seigel แนะนำสำหรับการตีความมาสก์
จากประสบการณ์ของเราด้วยวิธีการนี้เราสามารถรับรายการคอลัมน์ที่เปลี่ยนแปลงทั้งหมดจากแถว 10k cdc ภายในไม่เกิน 3 วินาที