Veri Yakalamayı ve __ $ update_mask ikili dosyasını değiştirin


9

Üretim tablosunda yapılan değişiklikleri yakalamak için CDC kullanıyoruz. Değiştirilen satırlar bir veri ambarına (bilgi) aktarılıyor. __ $ update_mask sütununun hangi sütunları varbinary formda güncellendiğini depoladığını biliyorum. Ayrıca, bu sütunlardan ne olduğunu öğrenmek için çeşitli CDC işlevlerini kullanabileceğimi de biliyorum .

Sorum şu. Depoda değiştirilen sütunları tanımlayabilmemiz için o maskenin arkasındaki mantığı kimse tanımlayabilir mi? Sunucu dışında işlem yaptığımız için bu MSSQL CDC işlevlerine kolay erişimimiz yok. Maskeyi kodda kendim bozmayı tercih ederim. Cdc işlevlerinin SQL ucundaki performansı bu çözüm için sorunludur.

Kısacası, değiştirilen sütunları __ $ update_mask alanından elle tanımlamak istiyorum.

Güncelleme:

Alternatif olarak, depoya değiştirilebilen sütunların insan tarafından okunabilir bir listesini göndermek de erişilebilirdi. Bunun, orijinal yaklaşımımızdan çok daha büyük bir performansla gerçekleştirilebileceğini tespit ettik.

Aşağıdaki bu soruya verilen CLR cevabı bu alternatifi karşılar ve maskeyi gelecekteki ziyaretçiler için yorumlamanın ayrıntılarını içerir. Bununla birlikte, XML PATH kullanarak kabul edilen cevap aynı nihai sonuç için en hızlısıdır.


Yanıtlar:


11

Ve hikayenin ahlaki ... test etmek, başka şeyler denemek, büyük düşünmek, sonra küçük, her zaman daha iyi bir yol olduğunu varsayın.

Bilimsel olarak son cevabım kadar ilginç. Başka bir yaklaşım denemeye karar verdim. XML PATH ('') hilesi ile birleşebileceğimi hatırladım. Önceki yanıttan captured_column listesinden her değişen sütunun sıralamasını nasıl alacağımı bildiğimden, MS bit işlevinin ihtiyacımız olan şey için bu şekilde daha iyi çalışıp çalışmadığını test etmeye değer olacağını düşündüm.

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

Tüm bu CLR'lerden çok daha temiz olsa da, yaklaşımı yalnızca yerel SQL koduna geri döndürür. Ve tambur rulosu ... aynı sonuçları bir saniyeden daha kısa sürede döndürür . Üretim verileri saniyede 100 kat daha büyük olduğu için önemlidir.

Diğer yanıtı bilimsel amaçlar için bırakıyorum - ancak şimdilik bu doğru cevabımız.


FROM yan tümcesinde tablo adına _CT ekleyin.
Chris Morley

1
Geri geldiğiniz ve cevapladığınız için teşekkürler, bir SQL çağrısı yapıldıktan sonra kod içinde buna göre filtreleyebilmemiz için çok benzer bir çözüm arıyorum. Ben CDC döndürülen her satırda her sütun için bir çağrı yapmak fantezi değilim!
nik0lias

2

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.


Bir çözümle geri döndüğünüz için teşekkürler, yakında bunun için kullanabilirim.
Mark Storey-Smith

Bunu yapmadan önce YENİ yanıtımı kontrol et . CLR kadar havalı ... daha da iyi bir yol bulduk. İyi şanslar.
RThomas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.