따라서 일부 조사를 마친 후에도 데이터웨어 하우스로 전달하기 전에 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 블록을 변수로 전달하고 마스크 필드를 _ $ update_mask 이진 필드마다 변경된 열의 쉼표로 구분 된 문자열을 반환하는 CLR 함수에 전달합니다. 이 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이 마스크를 해석하는 방식으로 제안한이 스택 오버 플로우 게시물 덕분에 .
이 접근법에 대한 경험에서 우리는 3 초 안에 10k cdc 행에서 변경된 모든 열의 목록을 얻을 수 있습니다.