Bu nedenle, bazı araştırmalardan sonra veri deposuna teslim etmeden önce bunu SQL tarafında yapmaya karar verdik. Ancak bu çok gelişmiş yaklaşımı alıyoruz (ihtiyaçlarımıza ve maskenin nasıl çalıştığına dair yeni anlayışa dayanarak).
Bu sorgu ile sütun adlarının ve sıra sıralarının bir listesini alırız. Dönüş, XML biçiminde geri gelir, böylece SQL CLR'ye geçebiliriz.
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);
Daha sonra bu XML bloğunu değişken olarak ve maske alanını _ $ update_mask ikili alanı başına değişen sütunların virgülle ayrılmış bir dizesini döndüren bir CLR işlevine geçiririz. Bu clr işlevi, xml listesindeki her sütun için değişiklik biti için maske alanını sorgular ve ardından adını ilgili sıradan döndürür.
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 kodu şuna benzer: (CDCUtilities adında bir derlemede derlenir)
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;
}
}
}
Ve CLR işlevi şöyle:
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]
Daha sonra bu sütun listesini satır kümesine ekler ve analiz için veri ambarına göndeririz. Sorgu ve clr kullanarak, değişiklik başına satır başına iki işlev çağrısı kullanmak zorunda kalacağız. Değişiklik yakalama örneğimiz için özelleştirilmiş sonuçlarla doğrudan ete geçebiliriz.
Jon Seigel tarafından maskeyi yorumlama şekli için önerilen bu yığın akışı postu sayesinde .
Bu yaklaşımla ilgili deneyimlerimize göre, 10k cdc satırlarındaki tüm değiştirilen sütunların bir listesini 3 saniyenin altında elde edebiliyoruz.