Voltar para o conteúdo

UPSERT sem duplicidade: o padrao minimo para carga incremental

Como pensar um UPSERT simples e seguro para cargas incrementais sem duplicar registros nem reprocessar a base inteira.

Raphael Carvalho · 07 de jun. de 2026 · 7 min de leitura

Resumo rápido

  • UPSERT existe para atualizar o que mudou e inserir o que chegou de novo.
  • Sem chave confiavel e sem deduplicacao na staging, o UPSERT vira gerador de problema em vez de solucao.
  • Um bom padrao minimo inclui staging, chave clara, criterio de recencia e validacao pos-carga.

Quando a base deixa de ser brinquedo e passa a atualizar todo dia, aparece uma pergunta inevitavel: vou reprocessar tudo ou atualizar so o que mudou?

E ai entra o UPSERT.

A promessa e boa: inserir novos registros e atualizar os antigos sem recriar a tabela inteira. O problema e que muita gente tenta aplicar esse padrao cedo demais, sem chave confiavel e sem staging organizada. O resultado costuma ser duplicidade, historico estranho e desconfiança sobre a base final.

O que um UPSERT deveria fazer

A ideia de um UPSERT e simples:

  • se a chave ainda nao existe, insere;
  • se a chave ja existe, atualiza.

No papel, parece trivial. Na pratica, ele so e seguro quando tres coisas estao claras:

  1. qual e a chave do registro;
  2. qual versao deve vencer em caso de conflito;
  3. qual e a regra para registros apagados, atrasados ou corrigidos.

Sem isso, voce nao tem carga incremental. Tem loteria incremental.

O erro classico: confiar na origem crua

Um erro comum e aplicar MERGE direto sobre uma tabela de origem que ja chega com duplicidade, atraso ou linha corrigida.

Se a staging contem duas versoes do mesmo pedido, por exemplo, o banco pode atualizar duas vezes, falhar por conflito ou consolidar algo que voce nem queria manter.

Antes do UPSERT, limpe a staging.

Um padrao minimo costuma incluir:

  • deduplicar pela chave de negocio;
  • usar uma coluna de recencia, como updated_at;
  • manter so a linha mais recente por chave antes da escrita final.

Um modelo mental simples

Pense em tres camadas:

  • bruta: recebe a origem como ela vem;
  • staging tratada: organiza, padroniza e deduplica;
  • tabela final: recebe o UPSERT.

Esse desenho e importante porque separa ingestao de decisao. A base bruta guarda a verdade operacional. A staging decide o que e valido para escrita. A tabela final serve o consumo analitico.

Quando tudo acontece em uma etapa so, fica mais dificil debugar.

Exemplo de raciocinio

Imagine uma tabela de pedidos cuja chave e pedido_id.

Voce recebe um lote diario com:

  • pedidos novos;
  • pedidos antigos com status alterado;
  • alguns registros repetidos pela API.

O fluxo minimo seria:

  1. carregar o lote bruto;
  2. deduplicar por pedido_id, mantendo o updated_at mais recente;
  3. aplicar MERGE na tabela final.

Em SQL, a ideia fica parecida com isto:

MERGE INTO pedidos_final AS destino
USING staging_pedidos AS origem
  ON destino.pedido_id = origem.pedido_id
WHEN MATCHED THEN
  UPDATE SET
    status = origem.status,
    valor = origem.valor,
    updated_at = origem.updated_at
WHEN NOT MATCHED THEN
  INSERT (pedido_id, status, valor, updated_at)
  VALUES (origem.pedido_id, origem.status, origem.valor, origem.updated_at);

O MERGE e so a ponta visivel. O que protege de verdade e a qualidade da origem.

Nao esqueça dos apagados

Nem toda fonte manda um “delete” explicito. As vezes um registro some. As vezes muda de status. As vezes vira inativo.

Por isso, o UPSERT sozinho nao cobre todos os cenarios.

Voce precisa decidir:

  • registro ausente no lote significa exclusao?
  • significa atraso de ingestao?
  • significa que o lote e parcial?

Essa resposta muda a logica de carga e a leitura de negocio. O pior erro aqui e assumir sem validar com quem conhece a origem.

Validacao pos-carga nao e opcional

Depois do UPSERT, sempre confira pelo menos:

  • quantidade de chaves unicas na tabela final;
  • duplicidade por chave;
  • quantos registros foram inseridos;
  • quantos foram atualizados;
  • se alguma metrica de controle saiu do intervalo esperado.

Se a base final passou a ter mais de uma linha por chave, sua logica de incremental falhou.

Quando nao vale insistir

Existe um ponto pragmatico importante: nem toda base precisa de UPSERT.

Se o volume e pequeno, a janela e curta e o reprocessamento completo custa pouco, talvez uma carga full refresh ainda seja melhor. Menos elegante, mas mais robusta.

O erro e adotar incremental so porque parece mais avancado.

Resumo direto

UPSERT bom nao comeca no MERGE. Ele comeca na definicao da chave, na limpeza da staging e na regra de recencia.

Se voce quer um padrao minimo seguro, lembre desta sequencia:

origem bruta -> staging deduplicada -> UPSERT -> validacao pos-carga

Isso ja evita boa parte das duplicidades que fazem times perderem horas desconfiando da tabela final.

SQL para criação de tabelas e ETL

Aprenda a criar estruturas, manipular dados e montar fluxos simples de ETL em SQL.

Avançar em SQL operacional

Perguntas frequentes

UPSERT e sempre a melhor opcao?

Nao. Em algumas bases pequenas, uma carga completa ainda pode ser mais simples. O ponto e escolher conscientemente.

Qual o maior risco do UPSERT?

Duplicidade ou sobrescrita errada causada por chave mal definida ou staging suja.

Preciso de MERGE para fazer UPSERT?

Nao obrigatoriamente. MERGE ajuda bastante, mas a logica tambem pode ser implementada com etapas separadas dependendo do banco.

Curso em português para brasileiros

SQL do Zero ao Avançado

A plataforma interativa de SQL feita para analistas. Pare de depender da fila de engenharia de dados.

Conheça o curso

Advertisement: SQL do Zero ao Avançado

Sobre o autor

Raphael Carvalho

Founder & Principal Consultant

Compartilhar Twitter LinkedIn

Leituras recomendadas