Alguém corrige o cadastro de um cliente numa segunda de manhã. Muda a cidade, muda o segmento, salva e vai tomar café. Ninguém comenta o assunto.
Na sexta, o gerente comercial abre o relatório de vendas por região do ano passado e vê um número diferente do que viu em janeiro. Nenhum pipeline falhou. Nenhuma execução deu erro. Nada foi apagado por engano.
O problema é que a tabela de origem não guarda o que era verdade antes. Ela guarda o que é verdade agora. E o seu pipeline copiou isso fielmente, por cima do passado.
Neste artigo, explicamos o que cada modo de sincronização faz com a tabela de destino e o que é SCD tipo 2 em termos de linha e de coluna. Também mostramos o que ele custa e como escolher tabela a tabela.
O que o espelhamento faz — e o que ele apaga junto
Espelhar é manter no destino uma cópia fiel do estado atual da origem. Linha nova entra, linha alterada é atualizada, linha que sumiu é removida. Ao fim de cada execução, origem e destino contam a mesma coisa.
Para a maior parte das tabelas isso é exatamente o que você quer. Um pedido cancelado deve sumir do destino. Um endereço corrigido deve aparecer corrigido. Ninguém abre o painel de estoque querendo saber quantas unidades havia em julho.
O que o espelhamento de banco de dados não faz é guardar o que estava ali antes. Ele não tem onde. Cada linha do destino tem lugar para uma versão só, e a versão que fica é sempre a última. Na prática, o seu relatório histórico é reescrito toda madrugada, em silêncio.
O destino é uma foto do presente.
SCD tipo 2 é linha nova, não atualização
Slowly changing dimension (dimensão que muda lentamente) é o nome que Ralph Kimball deu, em 1996, ao problema de um atributo descritivo mudar com o tempo. Cliente muda de cidade, produto muda de categoria, vendedor muda de região. São mudanças raras, e é justamente por serem raras que ninguém percebe o estrago.
O tipo 2 resolve de um jeito só: nada é atualizado. Toda mudança gera uma linha nova, e a linha antiga é fechada com uma data de fim. A tabela passa a ter várias linhas por entidade, cada uma válida num intervalo de tempo.
Um cliente que mudou de cidade e depois de faixa de fidelidade fica assim:
| versão | id_cliente | cidade | faixa | válido de | válido até | ativa |
|---|---|---|---|---|---|---|
| 8801 | 4471 | São Paulo | Bronze | 10/01/2024 | 15/03/2025 | não |
| 9142 | 4471 | Belo Horizonte | Bronze | 15/03/2025 | 02/02/2026 | não |
| 9905 | 4471 | Belo Horizonte | Prata | 02/02/2026 | em aberto | sim |
Repare que o fim de uma versão é exatamente o início da seguinte. O intervalo é fechado no começo e aberto no fim, então não sobra buraco entre as linhas e nenhuma data cai em duas versões ao mesmo tempo.
Três coisas mudam de lugar. O id_cliente deixa de identificar a linha e passa a identificar a entidade, repetido em todas as versões. Quem identifica a linha é o par chave mais início de vigência — ou uma coluna de versão criada pela carga, quando a implementação gera uma. E a tabela de vendas passa a apontar para a versão, não para o cliente.
A venda de fevereiro de 2025 aponta para a versão de São Paulo. Ela continua sendo uma venda de São Paulo depois da mudança de cidade, depois da promoção de faixa, depois de tudo. É isso que o tipo 2 compra: o passado para de andar.
As implementações variam, o mecanismo é um só
Você vai encontrar o tipo 2 implementado de várias formas. O conceito é um só; o que varia é o conjunto de colunas de controle.
- Só marcador de versão ativa: barato e limitado. Você sabe qual versão vale hoje e não sabe quando cada uma valeu. Não responde "como estava em 15 de março".
- Só janela de vigência: mais expressivo. Exige comparar datas em toda consulta.
- Vigência mais marcador: o arranjo mais comum. A redundância é de propósito, porque filtrar pela versão ativa é bem mais barato que comparar duas datas.
- Número de versão ou chave substituta: uma coluna própria por linha, útil para a tabela de fatos apontar direto para a versão e para auditar a carga.
- Fim em aberto: nulo ou uma data-sentinela como 31/12/9999. A sentinela dispensa tratar nulo em toda comparação de data; o nulo é mais honesto sobre o que ainda não aconteceu. Quem guarda também o marcador de versão ativa raramente sente a diferença.
- Detecção por comparação ou por assinatura: a carga compara coluna a coluna, ou guarda uma assinatura das colunas monitoradas e compara só ela. Em tabela larga, a assinatura é mais rápida.
- Colunas monitoradas e não monitoradas: nem toda mudança merece versão. Telefone digitado errado é correção, não mudança de negócio.
Nomes diferentes, mesma ideia: fechar em vez de sobrescrever.
Os outros tipos, em uma frase cada
O tipo 2 é o mais usado, não o único. Kimball formalizou os demais na terceira edição do The Data Warehouse Toolkit, em 2013.
- Tipo 0: o valor original nunca muda. Data de cadastro, canal de origem, CPF.
- Tipo 1: sobrescrita pura. O SCD tipo 1 é o espelhamento, e serve para corrigir erro.
- Tipo 2: linha nova por versão, com vigência.
- Tipo 3: uma coluna a mais guarda o valor anterior. Guarda um passo de histórico, não todos.
- Tipo 4: o histórico sai da tabela principal e vai para uma tabela separada.
- Tipo 5: tipo 4 com um atalho para o perfil atual na tabela principal.
- Tipo 6: linha versionada que também carrega o valor atual, para responder às duas perguntas.
- Tipo 7: a tabela de fatos guarda duas chaves, e a consulta escolhe se quer o passado ou o agora.
Do 4 em diante, a motivação é quase sempre a mesma: o tipo 2 cresceu demais.
O que o tipo 2 custa
Ele resolve um problema real e cria três.
A tabela cresce. Uma dimensão com atributo volátil e alguns milhões de linhas vira um problema de armazenamento e de consulta. Vale medir antes de ligar o modo em tudo.
A junção errada duplica tudo. Se alguém juntar a tabela de vendas à de clientes só pelo id_cliente, cada venda vira três. É o erro mais comum de quem acabou de ligar o tipo 2, e ele não dá erro: dá número inflado com cara de certo.
Toda consulta de "agora" precisa de filtro. O painel que antes era um SELECT direto passa a exigir o filtro da versão ativa. Quem esquecer, soma o cliente três vezes.
As três consultas abaixo mostram a diferença. A primeira é a que infla o número:
sql · a junção certa depende da pergunta
-- errado: cada venda vira uma linha por versão do cliente SELECT c.cidade, SUM(v.valor) FROM vendas v JOIN clientes c ON c.id_cliente = v.id_cliente GROUP BY c.cidade; -- "como estava na época": a data da venda escolhe a versão SELECT c.cidade, SUM(v.valor) FROM vendas v JOIN clientes c ON c.id_cliente = v.id_cliente AND v.data_venda >= c.valido_de AND (v.data_venda < c.valido_ate OR c.valido_ate IS NULL) GROUP BY c.cidade; -- "como está hoje": filtra a versão ativa SELECT c.cidade, SUM(v.valor) FROM vendas v JOIN clientes c ON c.id_cliente = v.id_cliente AND c.ativa GROUP BY c.cidade;
São perguntas diferentes, com respostas diferentes e legítimas. O tipo 2 não escolhe por você — ele apenas torna as duas possíveis.
Exemplo prático
Uma rede de varejo reorganiza o mix e move 400 produtos de categoria em janeiro. Com espelhamento, comparar o faturamento por categoria com o do ano passado fica sem sentido: o ano passado é recalculado com a categoria de hoje. Com tipo 2, os dois anos continuam comparáveis, e dá para medir o que a mudança de mix fez com a margem. É a mesma tabela, com decisões opostas.
Como decidir, tabela a tabela
A decisão não é da conexão nem do pipeline. É de cada tabela, e às vezes de cada coluna.
Vale o tipo 2 quando o atributo entra em relatório histórico, quando alguém compara períodos por ele, ou quando existe obrigação de mostrar o que valia numa data. Cadastro de cliente, tabela de produtos, hierarquia comercial, tabela de preços.
Fique no espelhamento quando o passado do atributo não interessa a ninguém, quando a tabela é grande e volátil, ou quando o dado já é um evento com data própria. Pedido, movimentação de estoque, log de acesso.
Não existe modo melhor ou pior. Existe o modo que a pergunta do seu time exige — e a maior parte dos destinos acaba com tabelas nos dois.
Como isso aparece no Januss
No Januss o modo é escolhido tabela a tabela, dentro da mesma origem. Uma tabela em carga completa, outra em espelhamento, outra em histórico, no mesmo pipeline. O modo histórico é o SCD tipo 2.
Quando a tabela é marcada como histórico, o Januss cria o destino com quatro colunas de controle além das suas:
sql · colunas de controle do modo histórico
_januss_valid_from TIMESTAMP NOT NULL -- início da vigência _januss_valid_to TIMESTAMP NULL -- nulo enquanto a versão está aberta _januss_active BOOLEAN NOT NULL -- a versão ativa da chave _januss_synced_at TIMESTAMP NOT NULL -- quando esta linha foi escrita
A chave primária da tabela de destino passa a ser a sua chave mais o _januss_valid_from — é o par que identifica a versão, exatamente como na seção acima. Os nomes são os padrões: se _januss_ não combina com a convenção do seu destino, os cinco nomes de coluna de sistema são definidos na sua conta e valem para todos os pipelines.
Na carga seguinte, o motor compara a versão ativa com a linha que chegou da origem. Igual, não escreve nada — reentrega de mensagem não gera versão duplicada. Diferente, fecha a versão ativa e insere a nova na mesma transação: o _januss_valid_to da linha fechada recebe o mesmo instante do _januss_valid_from da nova. O encadeamento vem do commit, não da sorte do relógio.
Linha que some da origem também não é apagada. A versão ativa é fechada e a entidade fica sem versão aberta — o passado dela continua consultável.
Duas limitações que vale conhecer antes de ligar o modo. A comparação é sobre todas as suas colunas: não dá para escolher quais delas disparam versão nova, então o telefone digitado errado gera uma versão como qualquer outra mudança. E quando o mesmo registro muda várias vezes dentro do mesmo lote de captura, o Januss guarda a imagem final do lote, não cada passo intermediário. A fidelidade é por execução, não por evento.
Quer ver com o seu dado qual modo cada tabela pede? São 14 dias de teste, sem cartão de crédito: criar seu workspace.
Fontes
Os tipos de slowly changing dimension e a nomenclatura seguem o Kimball Group:
- Slowly Changing Dimensions, Part 2 — Kimball Group
- Design Tip #152: Slowly Changing Dimension Types 0, 4, 5, 6 and 7 — Kimball Group
Crie seu workspace em minutos.
Escolha o modo por tabela e veja o histórico se formar no seu destino, com vigência e versão ativa.