เปลี่ยนการดักจับข้อมูลและไบนารี __ $ update_mask


9

เรากำลังใช้ CDC เพื่อบันทึกการเปลี่ยนแปลงที่เกิดขึ้นกับตารางการผลิต แถวที่เปลี่ยนแปลงกำลังถูกส่งออกไปยังคลังข้อมูล (informatica) ฉันรู้ว่าคอลัมน์ __ $ update_mask เก็บคอลัมน์ใดที่ได้รับการปรับปรุงในรูปแบบ varbinary ฉันยังรู้ว่าฉันสามารถใช้ฟังก์ชั่น CDC ที่หลากหลายเพื่อค้นหาจากหน้ากากที่คอลัมน์เหล่านั้น

คำถามของฉันคือสิ่งนี้ ใครสามารถนิยามตรรกะเบื้องหลังหน้ากากนั้นให้ฉันเพื่อให้เราสามารถระบุคอลัมน์ที่เปลี่ยนแปลงไปในคลังสินค้าได้หรือไม่? เนื่องจากเรากำลังประมวลผลนอกเซิร์ฟเวอร์เราจึงไม่สามารถเข้าถึงฟังก์ชั่น MSSQL CDC เหล่านั้นได้อย่างง่ายดาย ฉันค่อนข้างจะพังหน้ากากตัวเองในรหัส ประสิทธิภาพของฟังก์ชั่น cdc ใน SQL end นั้นเป็นปัญหาสำหรับวิธีนี้

กล่าวโดยย่อฉันต้องการระบุคอลัมน์ที่มีการเปลี่ยนแปลงด้วยมือจากฟิลด์ __ $ update_mask

ปรับปรุง:

ในฐานะที่เป็นทางเลือกที่ส่งรายชื่อคอลัมน์ที่เปลี่ยนแปลงไปยังคลังสินค้าที่มนุษย์สามารถอ่านได้ เราพบว่าสิ่งนี้สามารถทำได้ด้วยประสิทธิภาพที่สูงกว่าวิธีดั้งเดิมของเรา

คำตอบ CLR สำหรับคำถามนี้ตรงกับทางเลือกนี้และรวมถึงรายละเอียดของการตีความหน้ากากสำหรับผู้เข้าชมในอนาคต อย่างไรก็ตามคำตอบที่ยอมรับได้โดยใช้ XML PATH นั้นเร็วที่สุดสำหรับผลลัพธ์สุดท้ายเดียวกัน


คำตอบ:


11

และคุณธรรมของเรื่องราวคือ ... ทดสอบลองสิ่งอื่น ๆ คิดใหญ่แล้วตัวเล็กเสมอถือว่ามีวิธีที่ดีกว่า

ที่น่าสนใจทางวิทยาศาสตร์เป็นคำตอบสุดท้ายของฉันคือ ฉันตัดสินใจลองวิธีอื่น ฉันจำได้ว่าฉันสามารถเชื่อมโยงกับเคล็ดลับ XML PATH ('') ได้ เนื่องจากฉันรู้วิธีรับลำดับของคอลัมน์ที่เปลี่ยนแปลงแต่ละรายการจากรายการ capt_column จากคำตอบก่อนหน้านี้ฉันคิดว่ามันจะคุ้มค่าที่จะทดสอบว่าฟังก์ชันบิต MS จะทำงานได้ดีขึ้นในแบบที่เราต้องการ

SELECT __$update_mask ,
        ( SELECT    CC.column_name + ','
          FROM      cdc.captured_columns CC
                    INNER JOIN cdc.change_tables CT ON CC.[object_id] = CT.[object_id]
          WHERE     capture_instance = 'dbo_OurTableName'
                    AND sys.fn_cdc_is_bit_set(CC.column_ordinal,
                                              PD.__$update_mask) = 1
        FOR
          XML PATH('')
        ) AS changedcolumns
FROM    cdc.dbo_MyTableName PD

เป็นวิธีที่สะอาดกว่า (แม้ว่าจะไม่สนุกเท่า) CLR ทั้งหมดนั้นส่งคืนวิธีกลับไปที่โค้ด SQL ดั้งเดิมเท่านั้น และม้วนกลอง .... ส่งกลับผลลัพธ์เดียวกันในน้อยกว่าที่สอง เนื่องจากข้อมูลการผลิตใหญ่กว่า 100 เท่าทุกวินาที

ฉันจะทิ้งคำตอบอื่นไว้เพื่อจุดประสงค์ทางวิทยาศาสตร์ - แต่สำหรับตอนนี้นี่คือคำตอบที่ถูกต้องของเรา


ผนวก _CT กับชื่อตารางในส่วนคำสั่ง FROM
Chris Morley

1
ขอขอบคุณที่กลับมาตอบคำถามนี้ฉันกำลังมองหาโซลูชันที่คล้ายกันมากดังนั้นเราจึงสามารถกรองได้ตามรหัสภายในเมื่อมีการโทร SQL ฉันไม่อยากโทรทุกคอลัมน์ในทุกแถวที่ส่งคืนจาก CDC!
nik0lias

2

ดังนั้นหลังจากการวิจัยบางอย่างเราตัดสินใจที่จะยังคงทำสิ่งนี้ในฝั่ง 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 วินาที


ขอบคุณที่กลับมาพร้อมโซลูชันฉันอาจจะใช้ในเร็ว ๆ นี้
Mark Storey-Smith

ตรวจสอบคำตอบใหม่ของฉันก่อนที่จะทำ เท่อย่างที่ CLR คือ ... เราพบวิธีที่ดีกว่า โชคดี.
RThomas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.