Jadi, setelah beberapa penelitian kami memutuskan untuk tetap melakukan ini di sisi SQL sebelum menyerahkan ke gudang data. Tetapi kami mengambil pendekatan yang jauh lebih baik ini (berdasarkan kebutuhan dan pemahaman baru tentang cara kerja topeng).
Kami mendapatkan daftar nama kolom dan posisi ordinal mereka dengan pertanyaan ini. Pengembalian kembali dalam format XML sehingga kita bisa beralih ke 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);
Kami kemudian meneruskan blok XML itu sebagai variabel dan bidang mask ke fungsi CLR yang mengembalikan string kolom yang koma yang diubah per bidang biner _ $ update_mask. Fungsi clr ini menginterogasi bidang mask untuk bit perubahan untuk setiap kolom dalam daftar xml dan kemudian mengembalikan namanya dari ordinal terkait.
SELECT cdc.udf_clr_ChangedColumns(@colListXML,
CAST(__$update_mask AS VARCHAR(MAX))) AS changed
FROM cdc.dbo_OurCaptureTableName
WHERE NOT __$update_mask IS NULL;
Kode c # clr terlihat seperti ini: (dikompilasi ke dalam rakitan yang disebut 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;
}
}
}
Dan fungsi untuk CLR seperti ini:
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]
Kami kemudian menambahkan daftar kolom ini ke rowset dan meneruskannya ke gudang data untuk dianalisis. Dengan menggunakan kueri dan CLR kami menghindari keharusan menggunakan dua panggilan fungsi per baris per perubahan. Kita dapat langsung beralih ke daging dengan hasil yang disesuaikan untuk instance tangkapan perubahan kita.
Berkat posting stackoverflow ini disarankan oleh Jon Seigel untuk cara yang menafsirkan topeng.
Dalam pengalaman kami dengan pendekatan ini, kami dapat memperoleh daftar semua kolom yang diubah dari baris 10k cdc dalam waktu kurang dari 3 detik.