डेटा कैप्चर और __ $ अपडेट_मास्क बाइनरी बदलें


9

हम उत्पादन तालिका में किए गए परिवर्तनों को पकड़ने के लिए सीडीसी का उपयोग कर रहे हैं। परिवर्तित पंक्तियों को एक डेटा वेयरहाउस (सूचनात्मक) को निर्यात किया जा रहा है। मुझे पता है कि __ $ update_mask कॉलम स्टोर करता है कि कॉलम किस रूप में एक भिन्न रूप में अपडेट किए गए थे। मुझे यह भी पता है कि मैं उस मास्क से पता लगाने के लिए विभिन्न प्रकार के सीडीसी फ़ंक्शन का उपयोग कर सकता हूं कि वे कॉलम क्या थे।

मेरा सवाल यह है। क्या कोई मेरे लिए उस मुखौटे के पीछे के तर्क को परिभाषित कर सकता है ताकि हम उन स्तंभों की पहचान कर सकें जिन्हें गोदाम में बदल दिया गया था? चूंकि हम सर्वर के बाहर प्रोसेसिंग कर रहे हैं इसलिए हमारे पास उन MSSQL CDC फंक्शन्स के लिए आसान पहुँच नहीं है। मैं कोड में नकाब को ही तोड़ दूंगा। SQL अंत पर cdc फ़ंक्शन का प्रदर्शन इस समाधान के लिए समस्याग्रस्त है।

संक्षेप में, मैं __ $ अद्यतन_मास्क क्षेत्र से हाथ से परिवर्तित स्तंभों की पहचान करना चाहता हूं।

अपडेट करें:

एक वैकल्पिक रूप से गोदाम पर परिवर्तित स्तंभों की एक मानव पठनीय सूची भेजने के लिए भी अकूत था। हमने पाया कि यह हमारे मूल दृष्टिकोण से कहीं अधिक प्रदर्शन के साथ किया जा सकता है।

नीचे दिए गए इस सवाल का सीएलआर उत्तर इस विकल्प से मिलता है और इसमें भविष्य के आगंतुकों के लिए मुखौटा की व्याख्या करने का विवरण शामिल है। हालाँकि XML PATH का उपयोग करके स्वीकृत उत्तर समान अंतिम परिणाम के लिए सबसे तेज़ है।


जवाबों:


11

और कहानी का नैतिक है ... परीक्षण करें, अन्य चीजों की कोशिश करें, बड़ा सोचें, फिर छोटा, हमेशा मान लें कि एक बेहतर तरीका है।

मेरे अंतिम उत्तर के रूप में वैज्ञानिक रूप से दिलचस्प था। मैंने एक दूसरे दृष्टिकोण की कोशिश करने का फैसला किया। मुझे याद है कि मैं XML पथ ('') ट्रिक के साथ कॉनैट कर सकता था। चूँकि मैं जानता था कि पिछले उत्तर से कैप्चर किए गए_ कॉलम की सूची से प्रत्येक बदले हुए कॉलम का अध्यादेश कैसे प्राप्त होता है, तो मुझे लगता है कि यह परीक्षण के लायक होगा कि एमएस बिट फ़ंक्शन उस तरह से बेहतर काम करेगा जो हमें चाहिए।

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

यह उस तरह से क्लीनर है (हालांकि उतना मज़ेदार नहीं है) जो सीएलआर है, वह दृष्टिकोण केवल मूल एसक्यूएल कोड पर वापस करता है। और, ड्रम रोल .... उसी परिणाम को एक सेकंड से भी कम समय में वापस करता है । चूंकि उत्पादन डेटा हर दूसरी गणना से 100 गुना बड़ा है।

मैं वैज्ञानिक उद्देश्यों के लिए अन्य उत्तर छोड़ रहा हूं - लेकिन अभी के लिए, यह हमारा सही उत्तर है।


FROM क्लॉज में तालिका का नाम जोड़ें।
क्रिस मोरले

1
वापस आने और इसका जवाब देने के लिए धन्यवाद, मैं एक बहुत ही समान समाधान की तलाश कर रहा हूं ताकि हम एक एसक्यूएल कॉल किए जाने के बाद कोड के अनुसार इसे फ़िल्टर कर सकें। मुझे उम्मीद नहीं है कि सीडीसी से लौटी हर पंक्ति पर हर कॉलम के लिए एक कॉल किया जाएगा!
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;
        }
    }
}

और इस तरह सीएलआर को कार्य:

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 का उपयोग करके हम प्रति परिवर्तन प्रति पंक्ति दो फ़ंक्शन कॉल का उपयोग करने से बचते हैं। हम अपने परिवर्तन कैप्चर उदाहरण के लिए अनुकूलित परिणामों के साथ मांस को सही छोड़ सकते हैं।

जॉन सीगल द्वारा सुझाई गई इस स्टैकओवरफ्लो पोस्ट के लिए धन्यवाद जिसमें मुखौटा की व्याख्या की गई थी।

इस दृष्टिकोण के साथ हमारे अनुभव में हम 3 सेकंड के तहत 10k cdc पंक्तियों से सभी परिवर्तित स्तंभों की एक सूची प्राप्त करने में सक्षम हैं।


एक समाधान के साथ लौटने के लिए धन्यवाद, मैं जल्द ही इसके लिए उपयोग कर सकता हूं।
मार्क स्टोरी-स्मिथ

आप करने से पहले मेरा नया उत्तर देखें। सीएलआर के रूप में के रूप में अच्छा है ... हम भी एक बेहतर तरीका मिल गया। सौभाग्य।
RTHomas
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.