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

Como validar checksums em ELT para detetar regressões

João Barros 06 de October de 2026 6 min de leitura

Este tutorial mostra como implementar e validar checksums (hashes) em ELT para detetar regressões e corrupções nos dados durante cargas. Validar checksums ajuda a garantir integridade, facilita reprocessamento idempotente e reduz surpresas em produção. Vou explicar o porquê de cada passo e dar exemplos concretos: por exemplo, como gerir um conjunto de 100k–1M de linhas e que métricas monitorizar (percentagem de alteração, tempo de processamento).

Pré-requisitos

  • Ambiente com SQL (ex.: Azure Synapse, Databricks SQL, PostgreSQL) ou Spark/Databricks.
  • Uma tabela de origem com colunas simples (strings/números) ou ficheiros Parquet/CSV. Idealmente começa com uma tabela pequena (~10k linhas) para validar o processo antes de escalar para 100k–1M.
  • Permissão para criar tabelas temporárias e executar funções de hash (MD5/SHA1/SHA256). SHA256 é recomendado por ser resistente a colisões (256 bits).
  • Conhecimentos básicos de SQL ou PySpark. No mínimo saber executar SELECT, JOIN e criar tabelas simples.

Passo 1: Escolher a estratégia de checksum

Decidir se o checksum será por linha ou por partição/ficheiro. O mais comum é gerar um hash por linha baseado nas colunas que representam a chave de negócio e os atributos a proteger. Para tabelas com 100k–1M linhas, o cálculo por linha é fácil de armazenar (uma coluna adicional) e permite detetar quais registos mudaram.

Evita incluir colunas com timestamps de ingestão se quiseres idempotência: se incluíres um campo como ingestion_time cada execução produzirá checksums diferentes mesmo sem alterações lógicas. Alternativa: gerar dois checksums — row_checksum (atributos de negócio) e ingest_checksum (inclui metadata) — para diagnóstico.

Passo 2: Gerar checksum por linha em SQL

Exemplo em SQL usando SHA256 concatenando colunas em ordem determinística. Inclui COALESCE para valores NULL e separador consistente. Nota: SHA256 produz 256 bits; a probabilidade de colisão é negligível para volumes habituais (<10^9 linhas).

-- Exemplo SQL (adaptar nome de tabela/colunas)
SELECT
  id,
  col1,
  col2,
  col3,
  LOWER(CONVERT(VARCHAR(64), HASHBYTES('SHA2_256',
      CONCAT(COALESCE(col1,''|'') , '||' , COALESCE(CAST(col2 AS VARCHAR),''), '||' , COALESCE(col3,''))
  ), 2)) AS row_checksum
FROM source_table;

Em sistemas onde HASHBYTES não suporta grandes strings, serializa apenas os campos críticos (ex.: 5–10 colunas) ou usa funções nativas como sha2 em Databricks. Testa com uma amostra de 1k–10k linhas para medir tempo: em clusters modestos gerar 100k checksums SHA256 costuma demorar entre 10–60s.

Passo 3: Gerar checksum por linha em PySpark

Exemplo em PySpark/Databricks usando SHA2. Mantém a ordem das colunas e trata NULLs explicitamente. PySpark escala bem: um cluster com 4 cores pode processar 1M linhas em 1–3 minutos dependendo da complexidade das transformações.

# Exemplo PySpark
from pyspark.sql.functions import sha2, concat_ws, coalesce, lit, col

df = spark.table('source_table')
df_with_checksum = df.withColumn('row_checksum',
    sha2(concat_ws('||', coalesce(col('col1'), lit('')), coalesce(col('col2').cast('string'), lit('')), coalesce(col('col3'), lit(''))), 256)
)
df_with_checksum.createOrReplaceTempView('source_with_checksum')

Passo 4: Armazenar checksums e metadados

Crias uma tabela de auditoria que guarda chave, checksum, source_run_id e source_timestamp. Guarda também o número de linhas e um hash global por partição/ficheiro para diagnosticar corrupções de ficheiros grandes. Por exemplo, para 1M linhas, uma tabela audit_checksums com 1M registos é trivial de armazenar em formato columnar (Parquet/Delta).

CREATE TABLE audit_checksums (
    id STRING,
    row_checksum STRING,
    source_run_id STRING,
    source_timestamp TIMESTAMP
);

INSERT INTO audit_checksums
SELECT id, row_checksum, 'run_20261006_01', CURRENT_TIMESTAMP FROM source_with_checksum;

Exemplo de hash global por partição: agregas os row_checksums ordenados e calculas um hash final. Isto deteta diferenças ao nível do ficheiro mesmo que o número de linhas seja igual.

Passo 5: Comparar checksums entre execuções

Para detetar regressões fazes um JOIN entre a tabela actual e a tabela de auditoria anterior: checksum diferente => alteração; falta na actual => eliminação; ausência na anterior => nova linha. Analisa percentagens: por exemplo, se >0.1% das linhas aparecerem como CHANGED num processo diário pode ser aceitável em alguns cenários, mas se for >1% merece investigação.

-- Exemplo SQL de comparação
WITH prev AS (SELECT id, row_checksum AS prev_checksum FROM audit_checksums WHERE source_run_id = 'run_prev'),
     curr AS (SELECT id, row_checksum AS curr_checksum FROM source_with_checksum)
SELECT
  COALESCE(curr.id, prev.id) AS id,
  CASE
    WHEN prev.id IS NULL THEN 'NEW'
    WHEN curr.id IS NULL THEN 'DELETED'
    WHEN prev.prev_checksum != curr.curr_checksum THEN 'CHANGED'
    ELSE 'UNCHANGED'
  END AS status
FROM prev FULL OUTER JOIN curr ON prev.id = curr.id;

Passo 6: Lidar com falsos positivos e normalização

Erros comuns: diferenças por ordem de colunas, espaços em branco, tipos diferentes ou colunas irrelevantes. Normaliza strings (trim, lower), ordena colecções antes de serializar e converte tipos para evitar falsos positivos. Por exemplo, usa LOWER(TRIM(col_name)) e converte decimais para formato com precisão fixa antes de hash.

-- Normalização simples em SQL
LOWER(TRIM(col_name))
-- Em PySpark usa trim(lower(col('col')))

Também define um limiar para alertas automáticos: por exemplo, se NEW + DELETED + CHANGED > 0.5% das linhas diárias, cria um alerta e lança um job de validação aprofundada.

Verificar o resultado

Valida executando a comparação entre duas corridas de exemplo: introduz uma alteração intencional numa linha e verifica se o status aparece como CHANGED; remove uma linha e verifica DELETED; adiciona uma linha e verifica NEW. Confirma também que rows com só o timestamp de ingestão diferente aparecem como UNCHANGED se esse campo não for incluído no checksum. Regista métricas: tempo de cálculo, número de alterações e percentagem relativa para construir um histórico (p.ex. média diária de 0.03% alterações).

Conclusão

Implementar checksums em ELT é uma técnica simples e poderosa para detetar regressões, validar integridade e facilitar reprocessamentos idempotentes. Próximos passos práticos: automatizar o carregamento dos audit logs, criar alertas quando a percentagem de mudanças exceder um limiar e gerar checksums por partição para validar ficheiros grandes. Dica: começa por proteger um conjunto pequeno de tabelas críticas (3–5 tabelas) e itera conforme encontras falsos positivos; em 2–4 semanas terás um mecanismo robusto que reduz investigações manuais e aumenta confiança nas cargas.