Change Tracking vs CDC: Qual o Melhor para Monitorar Mudanças no SQL Server?

Espero que estejam todos bem! 😊
No post de hoje vou falar sobre um assunto que sempre gera dúvidas entre DBAs e Desenvolvedores. Quando usar Change Tracking e quando usar Change Data Capture (CDC) para monitorar alterações nos dados?
O objetivo desse post é esclarecer as diferenças entre Change Tracking e CDC, com exemplos práticos que vocês podem testar no seu ambiente.
Antes de mais nada, vamos entender o básico. Change Tracking é uma funcionalidade do SQL Server que foi introduzida na versão 2008. Ela permite que você identifique quais linhas foram modificadas em uma tabela, mas não armazena os valores que foram alterados.
Pense no Change Tracking como um “detector de mudanças” simples. Ele apenas te avisa: “Olha, a linha com PK=123 foi alterada”, mas não te diz o que mudou nem qual era o valor anterior. É uma tecnologia leve, que adiciona pouco overhead ao sistema, e ideal para cenários onde você só precisa saber se algo mudou, não necessariamente o que mudou.
Vamos ver um exemplo prático.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -- Habilitando Change Tracking no banco ALTER DATABASE TesteChangeTracking SET CHANGE_TRACKING=ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON) Use TesteChangeTracking GO -- Criando uma tabela de exemplo CREATE TABLE Funcionarios ( ID INT IDENTITY(1,1) PRIMARY KEY, Nome VARCHAR(100), Salario DECIMAL(10,2), Departamento VARCHAR(50), DataContratacao DATE ) |
Agora vamos habilitar o Change Tracking no banco, veja como é simples
1 2 3 4 | -- Habilitando Change Tracking na tabela ALTER TABLE Funcionarios ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON) |

1 2 3 4 5 6 | -- Inserindo dados de teste INSERT INTO Funcionarios (Nome, Salario, Departamento, DataContratacao) VALUES ('João Silva', 5000.00, 'TI', '2023-01-15'), ('Maria Santos', 6000.00, 'RH', '2022-08-20'), ('Pedro Oliveira', 4500.00, 'Financeiro', '2023-03-10' |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | -- IMPORTANTE: Capturar a versão ANTES das mudanças DECLARE @versao_inicial BIGINT = CHANGE_TRACKING_CURRENT_VERSION() PRINT 'Versão inicial: ' + CAST(@versao_inicial AS VARCHAR(20)) -- Fazendo algumas alterações UPDATE Funcionarios SET Salario = 5500.00 WHERE ID = 1 DELETE FROM Funcionarios WHERE ID = 3 INSERT INTO Funcionarios (Nome, Salario, Departamento, DataContratacao) VALUES ('Ana Costa', 5200.00, 'Marketing', '2023-11-01') -- Agora vamos ver o que mudou (usando a versão capturada) SELECT CT.ID, CT.SYS_CHANGE_VERSION, CT.SYS_CHANGE_OPERATION, F.Nome, F.Salario, F.Departamento FROM CHANGETABLE(CHANGES Funcionarios, @versao_inicial) AS CT LEFT JOIN Funcionarios F ON CT.ID = F.ID |
Analisando o resultado acima : ID=1, OPERATION=’U’ (Update), ID=3, OPERATION=’D’ (Delete) e ID=4, OPERATION=’I’ (Insert).
Vejam que temos as operações, mas não sabemos qual era o salário anterior do João. Essa é a principal característica do Change Tracking, podemos ver que os dados mudaram, mas não conseguimos ver os dados anteriores a mudança.
Agora vamos falar do (CDC – Change Data Capture). O CDC é uma funcionalidade mais robusta, também introduzida no SQL Server 2008, mas nas edições Enterprise, Developer e Express (com limitações). O CDC não apenas detecta mudanças, mas também armazena os valores antes e depois da alteração. É como ter um “histórico completo” de todas as mudanças que aconteceram nos seus dados.
Porém, como tudo na vida, essa funcionalidade mais completa vem com um custo, maior consumo de espaço em disco, maior overhead no sistema e maior complexidade para gerenciar.
Vamos ver o CDC em ação:
1 2 3 4 5 6 7 | -- Primeiro, habilitamos CDC no banco (requer sysadmin) USE master GO EXEC sp_configure 'show advanced options', 1 RECONFIGURE EXEC sp_configure 'Agent XPs', 1 RECONFIGURE |
1 2 3 4 | USE TesteCDC GO -- Habilitando CDC no banco EXEC sys.sp_cdc_enable_db |

1 2 3 4 5 6 7 8 9 | -- Criando nossa tabela de teste CREATE TABLE Produtos ( ID INT IDENTITY(1,1) PRIMARY KEY, Nome VARCHAR(100), Preco DECIMAL(10,2), Categoria VARCHAR(50), Estoque INT, DataUltimaAtualizacao DATETIME2 DEFAULT GETDATE() ) |
1 2 3 4 5 6 | -- Habilitando CDC na tabela EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'Produtos', @role_name = NULL, @supports_net_changes = 1 |
Caso o seu SQL Server Agent não estiver Habilitado, você irá receber uma mensagem assim :
Confira se os serviços estão realmente ligados e refaça a operação se necessário.
Essa é a mensagem esperada
1 2 3 4 5 6 7 8 9 10 11 | -- Inserindo dados de teste INSERT INTO Produtos (Nome, Preco, Categoria, Estoque) VALUES ('Notebook Dell', 2500.00, 'Informática', 10), ('Mouse Logitech', 50.00, 'Informática', 100), ('Cadeira Gamer', 800.00, 'Móveis', 5) -- Fazendo alterações UPDATE Produtos SET Preco = 2300.00, Estoque = 8 WHERE ID = 1 UPDATE Produtos SET Estoque = 95 WHERE ID = 2 DELETE FROM Produtos WHERE ID = 3 |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | -- Consultando as mudanças no CDC SELECT __$operation, CASE __$operation WHEN 1 THEN 'Delete' WHEN2THEN'Insert' WHEN 3 THEN 'Update (antes)' WHEN 4 THEN 'Update (depois)' END AS Operacao, __$start_lsn, __$update_mask, ID, Nome, Preco, Categoria, Estoque FROM cdc.dbo_Produtos_CT ORDER BY __$start_lsn |

Veja a diferença. Com CDC temos acesso tanto aos valores antigos quanto aos novos. Para cada UPDATE, o CDC cria dois registros, um com os valores antes da alteração (operation=3) e outro com os valores depois (operation=4).
Maneiro, né?
Onde usar o Change Tracking e onde usar o CDC?
Agora que vimos as duas em ação, vou compartilhar exemplos de quando usar cada uma. O Change Tracking é ideal quando você precisa de sincronização de dados simples, ou seja, quando você só precisa saber quais registros mudaram para replicar para outro sistema. Também é a escolha certa quando o baixo overhead é fundamental, especialmente quando performance é crítica e você não pode abrir mão de velocidade. É muito útil para detecção de conflitos em cenários de sincronização bidirecional e em cenários de cache onde você precisa invalidar cache baseado em mudanças.
Exemplos de uso do Change Tracking
Sincronização com um BI onde você só precisa saber quais Funcionarios foram alterados para reprocessar os dados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | USE TesteChangeTracking -- Criando tabela de funcionários CREATE TABLE Funcionarios ( ID INT IDENTITY(1,1) PRIMARY KEY, Nome VARCHAR(100), Salario DECIMAL(10,2), Departamento VARCHAR(50), DataContratacao DATE ) -- Habilitando Change Tracking na tabela ALTER TABLE Funcionarios ENABLECHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON) |
1 2 3 4 5 6 7 8 9 10 11 12 | -- Inserindo dados iniciais INSERT INTO Funcionarios (Nome, Salario, Departamento, DataContratacao) VALUES ('João Silva', 5000.00, 'TI', '2023-01-15'), ('Maria Santos', 6000.00, 'RH', '2022-08-20'), ('Pedro Oliveira', 4500.00, 'Financeiro', '2023-03-10') -- 1. Primeiro, vamos ver o estado inicial SELECT 'Estado Inicial dos Funcionários' AS Situacao SELECT * FROM Funcionarios SELECT 'Versão atual do Change Tracking: ' + CAST(CHANGE_TRACKING_CURRENT_VERSION() AS VARCHAR(10)) AS Info |

1 2 3 4 5 6 7 8 9 | -- 2. Simulando última sincronização (salva em tabela de controle) CREATE TABLE ControleSync ( Sistema VARCHAR(50), UltimaVersao BIGINT, DataSync DATETIME2 ) INSERT INTO ControleSync VALUES ('SistemaBI', CHANGE_TRACKING_CURRENT_VERSION(), GETDATE()) |

Simulando atividades de usuários
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | -- 3. Agora vamos simular atividade no sistema (várias transações de usuários) PRINT '--- Simulando atividade de usuários no sistema ---' -- Usuario 1: Aumento de salário UPDATE Funcionarios SET Salario = 5800.00 WHERE Nome = 'João Silva' PRINT 'RH aprovou aumento para João Silva' -- Usuario 2: Novo funcionário contratado INSERT INTO Funcionarios (Nome, Salario, Departamento, DataContratacao) VALUES ('Carlos Mendes', 4800.00, 'TI', '2024-01-15') PRINT 'Novo funcionário Carlos contratado' -- Usuario 3: Mudança de departamento UPDATE Funcionarios SET Departamento = 'Financeiro' WHERE Nome = 'Maria Santos' PRINT 'Maria transferida para Financeiro' -- Usuario 4: Funcionário desligado DELETE FROM Funcionarios WHERE Nome = 'Pedro Oliveira' PRINT 'Pedro desligado da empresa' |
Sincronizando os dados
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | -- 4. Agora é hora da sincronização! Vamos ver o que mudou DECLARE @ultima_sync BIGINT = (SELECT UltimaVersao FROM ControleSync WHERE Sistema = 'SistemaBI') PRINT '--- RESULTADO DA SINCRONIZAÇÃO ---' PRINT 'Versão da última sync: ' + CAST(@ultima_sync AS VARCHAR(10)) PRINT 'Versão atual: ' + CAST(CHANGE_TRACKING_CURRENT_VERSION() AS VARCHAR(10)) -- 5. Identificando quais registros mudaram SELECT 'Mudanças detectadas pelo Change Tracking:' AS Titulo SELECT CT.ID, CASE CT.SYS_CHANGE_OPERATION WHEN 'I' THEN 'INSERIDO' WHEN'U'THEN'ALTERADO' WHEN 'D' THEN 'DELETADO' END AS TipoMudanca, CT.SYS_CHANGE_VERSION AS VersaoMudanca, F.Nome, F.Departamento, F.Salario FROM CHANGETABLE(CHANGES Funcionarios, @ultima_sync) AS CT LEFT JOIN Funcionarios F ON CT.ID = F.ID ORDER BY CT.SYS_CHANGE_VERSION -- 6. Para o sistema de BI, pegamos apenas os dados atuais dos registros alterados SELECT 'Dados para enviar ao BI:' AS FinalSync SELECT F.ID, F.Nome, F.Salario, F.Departamento, F.DataContratacao, 'Dados atualizados para sincronização' AS Status FROM Funcionarios F WHERE F.ID IN ( SELECT CT.ID FROM CHANGETABLE(CHANGES Funcionarios, @ultima_sync) AS CT WHERE CT.SYS_CHANGE_OPERATION IN ('I', 'U') -- Só inseridos e alterados ) -- 7. Atualizando controle para próxima sincronização UPDATE ControleSync SET UltimaVersao = CHANGE_TRACKING_CURRENT_VERSION(), DataSync = GETDATE() WHERE Sistema = 'BaseBI' PRINT 'Sincronização concluída! Sistema BI atualizado.' |
Enfim, existem N maneiras que você pode capturar os dados modificados.
Já o CDC é a escolha certa quando você precisa de auditoria completa, ou seja, quando você precisa saber exatamente o que mudou nos seus dados. É ideal para manter histórico de alterações, especialmente para compliance e regulamentações. Também é a melhor opção para replicação de dados complexa, quando você precisa aplicar as mesmas mudanças em outro sistema mantendo a integridade. E é excelente para análise de comportamento, quando você quer entender como os dados evoluem ao longo do tempo.
1 2 3 4 5 6 7 8 9 | -- Criando banco para CDC DROP DATABASE IF EXISTS TesteCDC CREATE DATABASE TesteCDC USE TesteCDC GO -- Habilitando CDC no banco EXEC sys.sp_cdc_enable_db |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -- Criando tabela de produtos CREATE TABLE Produtos ( ID INT IDENTITY(1,1) PRIMARY KEY, Nome VARCHAR(100), Preco DECIMAL(10,2), Categoria VARCHAR(50), Estoque INT, DataUltimaAtualizacao DATETIME2 DEFAULT GETDATE() ) -- Habilitando CDC na tabela EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'Produtos', @role_name = NULL, @supports_net_changes = 1 |
Inserindo dados iniciais e verificando o estado inicial dos produtos
1 2 3 4 5 6 7 8 9 10 | -- Inserindo dados iniciais INSERT INTO Produtos (Nome, Preco, Categoria, Estoque) VALUES ('Notebook Dell Alienware M16', 2500.00, 'Informática', 10), ('Mouse Logitech G903', 50.00, 'Informática', 100), ('Cadeira Gamer DXRacer MX1000', 800.00, 'Móveis', 5) -- 1. Vamos ver o estado inicial dos produtos SELECT 'Estado inicial da loja:' AS Situacao SELECT ID, Nome, Preco, Categoria, Estoque FROM Produtos |
Simulando atividades de usuários durante o dia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -- 2. Simulando atividade de vários usuários durante o dia PRINT '--- SIMULANDO ATIVIDADES DO DIA ---' -- 08:30 - Gerente ajusta preços para promoção matinal UPDATE Produtos SET Preco = 2200.00 WHERE Nome = 'Notebook Dell Alienware M16' PRINT '08:30 - Gerente: Promoção matinal no Notebook (-R$ 300)' -- 10:15 - Reposição de estoque UPDATE Produtos SET Estoque = 120 WHERE Nome = 'Mouse Logitech G903' PRINT '10:15 - Estoque: Reposição de mouses' -- 14:00 - Promoção especial de tarde UPDATE Produtos SET Preco = 1950.00 WHERE Nome = 'Notebook Dell Alienware M16' PRINT '14:00 - Marketing: Super promoção vespertina no Notebook (-R$ 250)' -- 14:30 - Cliente comprou e diminuiu estoque UPDATE Produtos SET Estoque = 115 WHERE Nome = 'Mouse Logitech G903' PRINT '14:30 - Vendas: 5 mouses vendidos' -- 16:00 - Correção de preço (erro de digitação) UPDATE Produtos SET Preco = 2100.00 WHERE Nome = 'Notebook Dell Alienware M16' PRINT '16:00 - Supervisor: Correção de preço do Notebook' -- 17:00 - Correção de preço (erro de digitação) UPDATE Produtos SET Preco = 2100.00 WHERE Nome = 'Mouse Logitech G903' PRINT '17:00 - Supervisor: Correção de preço do Notebook' -- 18:00 - Produto descontinuado DELETE FROM Produtos WHERE Nome = 'Cadeira Gamer DXRacer MX1000' PRINT '18:00 - Gestão: Cadeira Gamer descontinuada' |
Auditando as mudanças com o CDC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | -- 3. AUDITORIA COMPLETA DO CDC PRINT '' PRINT '=== RELATÓRIO DE AUDITORIA COMPLETA ===' -- QUERY 1: Histórico específico do Notebook (SEM duplicação) SELECT 'AUDITORIA: Histórico APENAS do Notebook Dell' AS Relatorio SELECT c.Nome, c.Preco AS PrecoRegistrado, FORMAT(sys.fn_cdc_map_lsn_to_time(c.__$start_lsn), 'dd/MM/yyyy HH:mm:ss') AS MomentoMudanca, CASE WHEN c.__$operation = 3 THEN 'Preço ANTES' WHEN c.__$operation = 4 THEN 'Preço DEPOIS' END AS TipoPreco FROM cdc.dbo_Produtos_CT c WHERE c.__$operation IN (3,4) AND c.__$update_mask = 0x04 -- Apenas mudanças de preço AND c.Nome LIKE '%Notebook%' -- APENAS Notebook ORDER BY c.__$start_lsn -- QUERY 2: Relatório de TODOS os produtos (diferente da anterior) SELECT 'RELATÓRIO: Resumo de preços alterados por produto' AS Relatorio SELECT DISTINCT c.Nome AS Produto, COUNT(CASE WHEN c.__$operation = 4 THEN 1 END) AS QtdAlteracoes, MIN(CASE WHEN c.__$operation = 3 THEN c.Preco END) AS PrecoInicial, MAX(CASE WHEN c.__$operation = 4 THEN c.Preco END) AS PrecoFinal FROM cdc.dbo_Produtos_CT c WHERE c.__$operation IN (3,4) AND c.__$update_mask = 0x04 GROUP BY c.Nome -- Auditoria de produtos removidos (para compliance) SELECT 'AUDITORIA: Produtos removidos do sistema' AS Relatorio SELECT DISTINCT c.ID, c.Nome, c.Preco AS UltimoPreco, c.Categoria, FORMAT(sys.fn_cdc_map_lsn_to_time(c.__$start_lsn), 'dd/MM/yyyy HH:mm:ss') AS DataRemocao FROM cdc.dbo_Produtos_CT c WHERE c.__$operation = 1 -- Apenas DELETEs ORDER BY c.ID -- Relatório executivo: resumo de atividades SELECT 'RESUMO : Atividades do dia' AS Relatorio SELECT COUNT(CASE WHEN c.__$operation = 2 THEN 1 END) AS ProdutosAdicionados, COUNT(CASE WHEN c.__$operation = 1 THEN 1 END) AS ProdutosRemovidos, COUNT(CASE WHEN c.__$operation IN (3,4) THEN 1 END) / 2 AS ProdutosAlterados, COUNT(DISTINCT c.ID) AS ProdutosAfetados FROM cdc.dbo_Produtos_CT c |
Vamos falar sobre as limitações de cada tecnologia porque isso é importante na hora de escolher. O Change Tracking não armazena valores históricos, os dados são perdidos após o período de retenção, não funciona bem com transações muito grandes e pode ter problemas com operações BULK. Por outro lado, seu overhead é muito baixo, aproximadamente 2-5% de overhead, e não requer espaço adicional significativo.
Já o CDC tem suas próprias limitações. Está disponível apenas nas edições Enterprise, Developer e Express, requer SQL Server Agent rodando, tem maior consumo de espaço em disco e pode impactar performance em ambientes com muitas escritas. Seu overhead é moderado a alto, variando de 10-30% dependendo do volume de mudanças, e requer monitoramento das tabelas de CDC para evitar crescimento excessivo.
Aqui estão alguns scripts que uso no dia a dia para monitorar essas tecnologias.
Para monitorar o Change Tracking :
1 2 3 4 5 6 | -- Verificando bancos com Change Tracking habilitado SELECT name AS BancoComChangeTracking FROM sys.databases WHERE DATABASEPROPERTYEX(name, 'IsChangeTrackingEna bled') = 1 |

Validando tabelas com o Change Tracking habilitado
1 2 3 4 5 6 7 | -- Verificando tabelas com Change Tracking habilitado SELECT OBJECT_NAME(object_id) AS tabela, is_track_columns_updated_on, min_valid_version, CHANGE_TRACKING_CURRENT_VERSION() AS versao_atual FROM sys.change_tracking_tables |

Validando bases com o CDC habilitado
1 2 | -- Verificando bancos com CDC habilitado SELECT name, is_cdc_enabled FROM sys.databases WHERE is_cdc_enabled = 1 |
Validando tabelas com o CDC habilitado
1 2 3 4 5 6 7 8 9 10 | -- Verificando tabelas com CDC SELECT s.name AS schema_name, t.name AS table_name, c.capture_instance, c.object_id, c.start_lsn FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id JOIN cdc.change_tables c ON t.object_id = c.source_object_id |
Monitorando espaço ocupado pelas tabelas do CDC
1 2 3 4 5 6 7 | -- Verificando espaço usado pelas tabelas CDC SELECT OBJECT_NAME(object_id) AS tabela_cdc, SUM(reserved_page_count) * 8.0 / 1024 AS tamanho_mb FROM sys.dm_db_partition_stats WHERE object_id IN (SELECT object_id FROM cdc.change_tables) GROUP BY object_id |
No Change Tracking, a limpeza é automática se você configurou AUTO_CLEANUP = ON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | -- Verificando jobs de limpeza do CDC SELECT j.name, j.enabled, ja.run_requested_date, ja.last_executed_step_date FROM msdb.dbo.sysjobs j JOIN msdb.dbo.sysjobactivity ja ON j.job_id = ja.job_id WHERE j.name LIKE 'cdc.%' -- Limpeza manual do CDC (se necessário) DECLARE @cleanup_lsn BINARY(10) SET @cleanup_lsn = sys.fn_cdc_get_min_lsn('dbo_Produtos') EXEC sys.sp_cdc_cleanup_change_table @capture_instance = 'dbo_Produtos', @low_water_mark = @cleanup_lsn |
Algumas dicas que podem te ajudar na decisão sobre qual escolher
- Use Change Tracking quando performance for um fator crítico, e você só precisar de uma sincronização simples, o seu ambiente tiver restrições de espaço em disco ou você estiver trabalhando na versão Standard.
- Use CDC quando precisar de uma auditoria completa da sua tabela com um histórico detalhado da mudança dos dados e para atender questões de compliance, mas levando em conta que pode afetar a performance do seu banco e aumentar consideravelmente o espaço em disco ocupado pelas tabelas.
Conclusão
No final das contas, tanto Change Tracking quanto CDC cumprem o que prometem, só que de formas diferentes. O Change Tracking é mais leve e direto ao ponto. Se você só precisa saber que algo mudou, ele resolve sem complicação. Já o CDC é mais pesado, mas te dá informação completa, quando você precisa de auditoria detalhada, ele entrega tudo que você precisa.
A chave é entender seu cenário específico e escolher a ferramenta certa para o trabalho certo.
Lembre-se de testar sempre em ambiente de homologação antes de implementar em produção.
E vocês? já tiveram experiências com essas tecnologias? Qual escolheram para seus projetos? Compartilhem nos comentários suas experiências e dúvidas!
Qualquer dúvida, deixem nos comentários que quando possível responderei.
Consultor SQL Server
Referencias :