AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 35936
Accepted
RThomas
RThomas
Asked: 2013-03-05 16:10:53 +0800 CST2013-03-05 16:10:53 +0800 CST 2013-03-05 16:10:53 +0800 CST

Change Data Capture e o binário __$update_mask

  • 772

Estamos usando o CDC para capturar as alterações feitas em uma tabela de produção. As linhas alteradas estão sendo exportadas para um data warehouse (informatica). Eu sei que a coluna __$update_mask armazena quais colunas foram atualizadas em um formulário varbinary. Também sei que posso usar uma variedade de funções CDC para descobrir a partir dessa máscara quais eram essas colunas.

Minha pergunta é esta. Alguém pode me definir a lógica dessa máscara para que possamos identificar as colunas que foram trocadas no armazém? Como estamos processando fora do servidor, não temos acesso fácil a essas funções MSSQL CDC. Prefiro apenas quebrar a máscara sozinho no código. O desempenho das funções cdc no final do SQL é problemático para esta solução.

Resumindo, gostaria de identificar manualmente as colunas alteradas no campo __$update_mask.

Atualizar:

Como alternativa, o envio de uma lista legível por humanos de colunas alteradas para o depósito também era aceitável. Descobrimos que isso poderia ser executado com desempenho muito maior do que nossa abordagem original.

A resposta do CLR para esta pergunta abaixo atende a esta alternativa e inclui detalhes de interpretação da máscara para futuros visitantes. No entanto, a resposta aceita usando XML PATH é a mais rápida para o mesmo resultado final.

sql-server-2008 change-data-capture
  • 2 2 respostas
  • 6690 Views

2 respostas

  • Voted
  1. Best Answer
    RThomas
    2013-03-08T10:32:20+08:002013-03-08T10:32:20+08:00

    E a moral da história é... teste, tente outras coisas, pense grande, depois pequeno, sempre assuma que existe uma maneira melhor.

    Tão cientificamente interessante quanto foi minha última resposta. Eu decidi tentar uma outra abordagem. Lembrei que poderia concatenar com o truque XML PATH(''). Como eu sabia como obter o ordinal de cada coluna alterada da lista capture_column da resposta anterior, pensei que valeria a pena testar se a função de bit MS funcionaria melhor dessa maneira para o que precisávamos.

    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
    

    É muito mais limpo do que (embora não tão divertido quanto) todo aquele CLR, retorna a abordagem de volta apenas ao código SQL nativo. E rufar de tambores... retorna os mesmos resultados em menos de um segundo . Como os dados de produção são 100 vezes maiores, cada segundo conta.

    Estou deixando a outra resposta para fins científicos - mas, por enquanto, esta é a nossa resposta correta.

    • 13
  2. RThomas
    2013-03-06T11:25:27+08:002013-03-06T11:25:27+08:00

    Então, depois de alguma pesquisa, decidimos ainda fazer isso no lado do SQL antes de passar para o data warehouse. Mas estamos adotando essa abordagem muito aprimorada (com base em nossas necessidades e em uma nova compreensão de como a máscara funciona).

    Obtemos uma lista dos nomes das colunas e suas posições ordinais com esta consulta. O retorno volta em um formato XML para que possamos passar para o 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);
    

    Em seguida, passamos esse bloco XML como uma variável e o campo de máscara para uma função CLR que retorna uma string delimitada por vírgula das colunas que foram alteradas pelo campo binário _$update_mask. Essa função clr interroga o campo de máscara para alterar o bit de cada coluna na lista xml e, em seguida, retorna seu nome do ordinal relacionado.

    SELECT  cdc.udf_clr_ChangedColumns(@colListXML,
            CAST(__$update_mask AS VARCHAR(MAX))) AS changed
        FROM cdc.dbo_OurCaptureTableName
        WHERE NOT __$update_mask IS NULL;
    

    O código c# clr se parece com isso: (compilado em um assembly chamado 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;
            }
        }
    }
    

    E a função para o CLR assim:

    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]
    

    Em seguida, anexamos essa lista de colunas ao conjunto de linhas e passamos para o data warehouse para análise. Ao usar a consulta e o clr, evitamos ter que usar duas chamadas de função por linha por alteração. Podemos ir direto ao ponto com resultados personalizados para nossa instância de captura de alterações.

    Obrigado a esta postagem stackoverflow sugerida por Jon Seigel pela maneira de interpretar mask.

    Em nossa experiência com essa abordagem, conseguimos obter uma lista de todas as colunas alteradas de 10 mil linhas cdc em menos de 3 segundos.

    • 2

relate perguntas

  • Melhores práticas para conectar bancos de dados que estão em diferentes regiões geográficas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Quanto "Padding" coloco em meus índices?

  • Existe um processo do tipo "práticas recomendadas" para os desenvolvedores seguirem para alterações no banco de dados?

  • Downgrade do SQL Server 2008 para 2005

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Conceder acesso a todas as tabelas para um usuário

    • 5 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve