O cadastro do CNPJ que a Receita publica todo mês é uma fotografia: mostra como cada empresa está, não o que mudou. Mas quem guarda as fotografias e as compara ganha algo que o arquivo sozinho não oferece: eventos. Uma filial aberta, uma troca de atividade principal, uma mudança de cidade ou a entrada de um novo sócio são acontecimentos que, muitas vezes, vêm acompanhados de novas necessidades, e portanto de novas compras.
Este artigo apresenta um catálogo de sinais que dá para extrair da comparação mensal, a leitura comercial de cada um e uma implementação em SQL (ClickHouse) que evita os falsos positivos mais comuns.
Por que diffs, e não só filtros
Um filtro responde "quais empresas são assim?". Um diff responde "quais empresas ficaram assim agora?". Para prospecção, a segunda pergunta costuma valer mais:
- o momento da mudança é quando a empresa está revendo fornecedores, sistemas e processos;
- uma lista de eventos recentes é pequena, específica e fácil de trabalhar com abordagem personalizada;
- o próprio evento é um bom motivo de contato ("vi que vocês abriram uma unidade em Campinas").
O custo é de engenharia: guardar os snapshots, normalizar os campos e calcular as diferenças com cuidado. Lembre também que o ritmo é mensal. O dump de outubro de 2026, por exemplo, foi publicado em 13 de outubro, então um evento que aconteceu no começo de setembro pode só aparecer agora.
O catálogo de sinais
| Sinal | Como detectar | Leitura comercial | Ruído típico |
|---|---|---|---|
| Nova empresa | Raiz que não existia no mês anterior | Demanda por tudo: contabilidade, banco, software, equipamentos | Empresas de fachada, MEIs (se não forem o seu público) |
| Nova filial | Novo CNPJ com raiz já existente | Expansão: TI, telecom, mobiliário, segurança, recrutamento | Filiais abertas e baixadas em sequência |
| Mudança de CNAE principal | Valor diferente entre os meses | Mudança de modelo de negócio, novos produtos ou serviços | Correções cadastrais sem mudança real |
| Novo CNAE secundário | Código presente agora e ausente antes | Diversificação, nova linha de receita | Inclusões "por garantia" |
| Mudança de município | Município diferente | Mudança de sede: novos fornecedores locais | Recodificação de tabelas, ajustes de CEP |
| Mudança de endereço na mesma cidade | CEP ou logradouro diferente | Mudança para espaço maior ou menor | Variações de grafia ("R." e "RUA") |
| Saída do Simples Nacional | Data de exclusão preenchida | Crescimento acima do teto, ou pendência fiscal | Exclusões por débito (sinal de risco) |
| Entrada ou saída de sócio | Linha de sócio nova ou removida | Sucessão, investimento, nova gestão | Ajustes de qualificação |
| Mudança de natureza jurídica | Ltda que vira S.A., por exemplo | Preparação para investimento ou governança | Raro, mas relevante |
| Mudança de situação | Ativa para inapta, suspensa ou baixada | Risco: limpar da base, rever crédito | Reativações após regularização |
Nenhum desses sinais é uma intenção de compra declarada. São gatilhos para olhar a empresa com mais atenção, e funcionam melhor combinados com o ICP: uma nova filial de uma empresa fora do seu perfil continua fora do perfil.
A base: snapshots versionados
O primeiro requisito é nunca sobrescrever o mês anterior. Uma tabela única, particionada por mês de referência, resolve isso de forma simples:
CREATE TABLE estab_snapshot
(
referencia Date,
cnpj FixedString(14),
cnpj_basico FixedString(8),
situacao LowCardinality(String),
cnae_principal LowCardinality(String),
cnaes_secundarios Array(LowCardinality(String)),
uf LowCardinality(String),
municipio LowCardinality(String),
cep String,
logradouro String,
numero String,
impressao UInt64 MATERIALIZED cityHash64(
situacao, cnae_principal, arraySort(cnaes_secundarios),
uf, municipio, cep, upper(trim(logradouro)), upper(trim(numero)))
)
ENGINE = MergeTree
PARTITION BY referencia
ORDER BY (cnpj, referencia);Dois detalhes fazem diferença:
- Particionar por mês permite descartar meses antigos com
DROP PARTITIONe comparar dois meses lendo só as partições necessárias. - A coluna
impressaoé um hash dos campos monitorados, calculado na inserção. Comparar um inteiro por linha é muito mais barato do que comparar dez colunas, e só as linhas com hash diferente seguem para a análise campo a campo.
Normalize antes de comparar
A maior fonte de falsos positivos não é a Receita mudar dados, e sim a mesma informação aparecer escrita de outro jeito. Por isso o hash acima já aplica:
upper(trim(...))nos campos de texto livre, para ignorar maiúsculas e espaços;arraySort(...)na lista de CNAEs secundários, para que a ordem não gere diferença.
Outras normalizações úteis, conforme o campo:
- expandir abreviações de logradouro ("R" para "RUA", "AV" para "AVENIDA");
- tratar
S/N,SNe vazio como o mesmo número; - remover pontuação do CEP;
- mapear códigos antigos quando uma tabela de domínio for recodificada.
Novas empresas e novas filiais
Com os snapshots no lugar, novos estabelecimentos são os CNPJs do mês atual que não existiam no anterior. A raiz diz se é uma empresa nova ou uma filial de uma empresa existente:
SELECT
n.cnpj,
n.uf,
n.cnae_principal,
if(n.cnpj_basico IN (SELECT cnpj_basico FROM estab_snapshot WHERE referencia = '2026-09-01'),
'nova_filial', 'nova_empresa') AS tipo
FROM (SELECT * FROM estab_snapshot WHERE referencia = '2026-10-01') AS n
LEFT ANTI JOIN (SELECT cnpj FROM estab_snapshot WHERE referencia = '2026-09-01') AS a
ON a.cnpj = n.cnpj;O LEFT ANTI JOIN devolve apenas as linhas da esquerda sem correspondência na direita, sem precisar de NOT IN sobre dezenas de milhões de valores.
Mudanças campo a campo
Para as linhas que existem nos dois meses e cujo hash mudou, transformamos cada campo monitorado numa tupla (campo, antes, depois) e mantemos só as que diferem:
SELECT cnpj, m.1 AS campo, m.2 AS antes, m.3 AS depois
FROM
(
SELECT
n.cnpj AS cnpj,
[
('situacao', a.situacao, n.situacao),
('cnae_principal', a.cnae_principal, n.cnae_principal),
('municipio', a.municipio, n.municipio),
('cnaes_secundarios', arrayStringConcat(arraySort(a.cnaes_secundarios), ','),
arrayStringConcat(arraySort(n.cnaes_secundarios), ','))
] AS mudancas
FROM (SELECT *, impressao FROM estab_snapshot WHERE referencia = '2026-10-01') AS n
INNER JOIN (SELECT *, impressao FROM estab_snapshot WHERE referencia = '2026-09-01') AS a
ON a.cnpj = n.cnpj
WHERE a.impressao != n.impressao
)
ARRAY JOIN mudancas AS m
WHERE m.2 != m.3
ORDER BY cnpj, campo;Repare no SELECT *, impressao: colunas MATERIALIZED não entram no * e precisam ser pedidas explicitamente.
O resultado é uma tabela de eventos com uma linha por mudança, pronta para gravar numa tabela eventos com a data de referência. O mesmo padrão vale para a tabela de empresas (capital social, natureza jurídica, porte), para o Simples (datas de exclusão) e para os sócios, comparando a chave composta de raiz, nome, documento mascarado e qualificação.
Um freio de segurança antes de publicar
Às vezes o arquivo de origem muda de layout, uma tabela de domínio é recodificada ou uma parte do dump chega incompleta. Se o pipeline publicar alertas nessas condições, os usuários recebem milhares de "mudanças" falsas de uma vez. Uma checagem simples evita isso:
SELECT
countIf(a.impressao != n.impressao) AS alterados,
count() AS comparados,
round(alterados / comparados * 100, 2) AS pct_alterados
FROM (SELECT cnpj, impressao FROM estab_snapshot WHERE referencia = '2026-10-01') AS n
INNER JOIN (SELECT cnpj, impressao FROM estab_snapshot WHERE referencia = '2026-09-01') AS a
ON a.cnpj = n.cnpj;Guarde esse percentual a cada carga. Se ele ficar muito acima do histórico, segure a publicação dos eventos e investigue. A mesma lógica vale para o número de novos estabelecimentos e de mudanças por campo.
Do evento à ação
Uma tabela de eventos só gera valor quando chega a quem vende. Algumas formas de entrega, da mais simples à mais integrada:
- Relatório mensal por segmento: novas filiais e mudanças de CNAE dentro do ICP, numa planilha.
- Alertas por lista monitorada: o vendedor acompanha suas contas e recebe um e-mail quando algo muda.
- Webhooks e integração com CRM: o evento cria uma tarefa ou atualiza um campo no CRM, com o motivo do contato.
- Pontuação dinâmica: eventos recentes aumentam a prioridade da conta por algumas semanas.
Em todos os casos, mostre o evento com contexto: o que mudou, de quanto para quanto e em qual mês de referência. "Mudou de São Paulo para Campinas no cadastro de outubro" é acionável; "houve uma alteração" não é.
Cuidados finais
- Eventos não são intenção. Use-os para priorizar e personalizar, não para disparar mensagens automáticas em massa.
- Sócios são pessoas. Entrada e saída de sócio pessoa física envolve dado pessoal; trate com os cuidados da LGPD e evite expor nomes onde não for necessário.
- Defasagem. Deixe claro que o evento foi detectado no cadastro de um mês específico, não na data em que aconteceu.
Conclusão
Guardar os snapshots mensais, normalizar antes de comparar, calcular um hash por linha e analisar campo a campo só o que mudou transforma um arquivo estático em um fluxo de sinais. Com um freio de segurança antes da publicação e uma entrega que chegue ao CRM, esses eventos viram motivos concretos de conversa com as empresas certas, no momento certo.
Para acompanhar mudanças em empresas monitoradas sem manter esse pipeline, experimente o monitoramento da CNPJpedia.
Experimente a busca na CNPJpedia
Filtre empresas por CNAE, região, porte e situação cadastral na base da Receita Federal. Comece pelo plano grátis.
Criar conta grátis