Змініть Захоплення даних та двійковий файл __ $ update_mask


9

Ми використовуємо CDC для зйомки змін, внесених до виробничої таблиці. Змінені рядки експортуються в сховище даних (інформатика). Я знаю, що стовпчик __ $ update_mask зберігає, які колонки були оновлені у варбінарній формі. Я також знаю, що я можу використовувати різні функції CDC, щоб дізнатися з цієї маски, що це за стовпці.

Моє запитання таке. Чи може хтось визначити для мене логіку цієї маски, щоб ми могли ідентифікувати стовпчики, які були змінені на складі? Оскільки ми обробляємось поза сервером, ми не маємо легкого доступу до цих функцій MSSQL CDC. Я вважаю за краще просто розбити маску в коді. Виконання функцій cdc на кінці SQL є проблематичним для цього рішення.

Коротше кажучи, я хотів би визначити змінені стовпці вручну з поля __ $ update_mask.

Оновлення:

Як альтернативне надсилання людського читаного списку змінених стовпців на склад також було доступним. Ми виявили, що це може бути виконано з продуктивністю, значно більшою, ніж наш оригінальний підхід.

Відповідь CLR на це запитання нижче відповідає цій альтернативі і включає деталі інтерпретації маски для майбутніх відвідувачів. Однак прийнята відповідь, використовуючи XML PATH, є найшвидшим досі за тим самим кінцевим результатом.


Відповіді:


11

І мораль історії полягає в тому, щоб ... перевірити, спробувати інші речі, думати про велике, а потім про мале, завжди вважай, що є кращий шлях.

Настільки ж науково цікавою, як і моя остання відповідь. Я вирішив спробувати ще один підхід. Я пам’ятав, що міг би зробити фокус із трюком XML PATH (''). Оскільки я знав, як отримати порядок кожного зміненого стовпця зі списку catch_column з попередньої відповіді, я вважав, що варто перевірити, чи краще функція MS bit буде працювати так, як нам потрібно.

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.
Кріс Морлі

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]

Потім додаємо цей список стовпців до набору рядків і передаємо на сховище даних для аналізу. Використовуючи запит та клір, ми уникаємо використання двох викликів функцій у рядку за зміну. Ми можемо пропустити прямо до м'яса з результатами, налаштованими на наш екземпляр збору змін.

Завдяки цій публікації про stackoverflow, запропонованій Джоном Сейгелем, для способу інтерпретації маски.

В нашому досвіді з таким підходом ми можемо отримати список всіх змінених стовпців з 10k рядків cdc протягом менше 3 секунд.


Дякуємо, що повернулися з рішенням, можливо, незабаром я скористаюся цим.
Марк Сторі-Сміт

Перевірте мою НОВУ відповідь, перш ніж робити. Холодно, як CLR ... ми знайшли ще кращий спосіб. Удачі.
Ртом
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.