(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como remover duplicados em ELT: passo a passo

João Barros 04 de July de 2026 4 min de leitura

Depois de carregar dados em bruto para a camada de staging, é normal encontrar registos duplicados: ficheiros reprocessados, tentativas repetidas de ingestão ou duplicados vindos da própria origem. Estes duplicados inflacionam métricas (uma venda contada duas vezes), partem junções (joins) e podem duplicar linhas em relatórios de Power BI, por isso convém tratá-los cedo. Veja a seguir, passo a passo, como remover duplicados em ELT na camada de transformação, com SQL padrão que funciona em SQL Server, Snowflake, BigQuery, Databricks e Microsoft Fabric.

Pré-requisitos

  • Uma tabela de staging já carregada — o "L" (Load) do ELT —, por exemplo staging.customers.
  • Permissões para executar SQL e criar tabelas ou vistas no seu data warehouse.
  • Saber qual é a chave de negócio que identifica um registo único (ex.: customer_id).
  • Uma coluna de data/hora para desempatar (ex.: updated_at).

Passo 1: Confirmar que existem duplicados

Antes de apagar seja o que for, confirme o problema. Agrupe pela chave de negócio e conte as linhas por chave: se alguma chave tiver mais do que uma linha, existem duplicados a tratar. Se a tabela for muito grande, corra primeiro esta verificação para dimensionar o problema antes de reescrever dados.

SELECT customer_id, COUNT(*) AS n_rows
FROM staging.customers
GROUP BY customer_id
HAVING COUNT(*) > 1
ORDER BY n_rows DESC;

Passo 2: Definir a chave e o critério de desempate

Para decidir qual das linhas repetidas manter, precisa de uma regra. O mais comum é ficar com o registo mais recente. Defina então dois elementos: a chave de negócio (o que torna um registo único) e a coluna que ordena do mais novo para o mais antigo, normalmente uma data de atualização como updated_at.

Atenção: SELECT DISTINCT só elimina linhas totalmente iguais. Não resolve duplicados de negócio, em que a mesma chave aparece com valores diferentes. Por isso usamos ROW_NUMBER().

Passo 3: Numerar as linhas com ROW_NUMBER()

A função de janela ROW_NUMBER() atribui um número sequencial a cada linha dentro de um grupo. Com PARTITION BY definimos o grupo (a chave de negócio) e com ORDER BY definimos quem fica em primeiro lugar. A linha que queremos manter recebe sempre o número 1.

Ao contrário de GROUP BY, que colapsa as linhas e obriga a agregar todas as colunas, a função de janela mantém intactas todas as colunas originais. É por isso que ROW_NUMBER() é ideal para deduplicar: escolhe uma linha inteira por chave, sem perder informação.

WITH ranked AS (
  SELECT
    customer_id, name, email, updated_at,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id
      ORDER BY updated_at DESC
    ) AS rn
  FROM staging.customers
)
SELECT customer_id, name, email, updated_at
FROM ranked
WHERE rn = 1;

Passo 4: Materializar a tabela sem duplicados

Agora guarde o resultado numa tabela limpa, pronta para as camadas seguintes. Use o padrão CREATE TABLE ... AS SELECT (CTAS) e liste as colunas finais, para não arrastar a coluna auxiliar rn.

CREATE TABLE clean.customers AS
WITH ranked AS (
  SELECT
    customer_id, name, email, updated_at,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id
      ORDER BY updated_at DESC
    ) AS rn
  FROM staging.customers
)
SELECT customer_id, name, email, updated_at
FROM ranked
WHERE rn = 1;

Alguns warehouses — Snowflake, BigQuery e Databricks — oferecem a cláusula QUALIFY, que filtra pela função de janela sem precisar da CTE e deixa o código mais curto:

SELECT customer_id, name, email, updated_at
FROM staging.customers
QUALIFY ROW_NUMBER() OVER (
  PARTITION BY customer_id
  ORDER BY updated_at DESC
) = 1;

Verificar o resultado

Confirme que a tabela final não tem duplicados repetindo a contagem do Passo 1, agora sobre a tabela limpa: o resultado deve vir vazio. Compare ainda o total de linhas antes e depois — a diferença corresponde exatamente aos duplicados removidos.

-- Não deve devolver nenhuma linha
SELECT customer_id, COUNT(*)
FROM clean.customers
GROUP BY customer_id
HAVING COUNT(*) > 1;

Conclusão

Com uma função de janela e um critério de desempate claro, a deduplicação torna-se um passo fiável e repetível da sua transformação ELT. O passo seguinte é integrar esta query no seu orquestrador — por exemplo Azure Data Factory ou dbt — para correr em cada carga e manter as camadas silver e gold sempre limpas. Fica a pergunta para o seu caso: qual deve ser o critério de desempate — a data mais recente, ou uma regra de prioridade por fonte?