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

Como criar uma Fact table de tipo 'Snapshot por Evento' em Modelação de Dados (Kimball)

João Barros 17 de September de 2026 5 min de leitura

Este tutorial mostra como criar uma Fact table de tipo 'Snapshot por Evento' em Modelação de Dados (Kimball): uma tabela de factos que regista o estado de uma entidade sempre que ocorre um evento relevante. É útil para analisar a evolução por evento (ex.: alterações de estado de encomendas) sem conflituar com snapshots periódicos nem com accumulating snapshots.

Pré-requisitos

  • Conhecimentos básicos de Modelação Dimensional (Fact/DIMensions).
  • Acesso a uma base de dados SQL (ex.: SQL Server, PostgreSQL).
  • Dados de origem com eventos (ex.: logs de estado de encomenda com timestamp).

Passo 1: Entender quando usar uma Snapshot por Evento

Uma Snapshot por Evento grava o estado da entidade cada vez que ocorre um evento que interessa para análise (ex.: alteração de estado, registo de pagamento). Use-a quando precisa do histórico por evento, sem perder o contexto temporal. Evita perda de granularidade que ocorreria com snapshots periódicos e permite análises de funnel e lead time entre eventos.

Passo 2: Definir a granularidade e as chaves

Decida a granularidade: geralmente uma linha por (entity_id, event_id/timestamp). Defina chaves:

  • Surrogate key da fact (fact_snapshot_id) — identidade da tabela de factos.
  • Natural keys: entity_id e event_timestamp (ou event_id se existir).
  • Foreign keys para dimensões conformadas (ex.: dim_date, dim_user, dim_location).
-- Exemplo de definição mínima em SQL (SQL Server/Postgres sintaxe geral)
CREATE TABLE fact_event_snapshot (
  fact_snapshot_id BIGSERIAL PRIMARY KEY,
  entity_id VARCHAR(50) NOT NULL,
  event_timestamp TIMESTAMP NOT NULL,
  event_type VARCHAR(50) NOT NULL,
  status_before VARCHAR(50),
  status_after VARCHAR(50),
  amount NUMERIC(18,2),
  dim_date_key INT, -- FK para dim_date
  dim_user_key INT, -- FK para dim_user
  load_batch_id VARCHAR(50) -- rastreio de carga
);

Passo 3: Mapear atributos e identificar dimensões

Escolha quais atributos vão para a fact (medidas e atributos degenerate) e quais vão para dimensões. Medidas: valores que se agregam (amount, quantity). Atributos de contexto que se repetem e têm cardinalidade baixa devem ir para dimensões (status, event_type pode ser dimensão pequena ou string direta na fact se for útil para performance).

Passo 4: Extração e transformação (ETL) — lógica de captura por evento

Implemente a lógica que detecta novos eventos e cria um snapshot por evento. Normalmente: ler source_events, para cada evento construir um registo com estado antes/depois e ligar chaves de dimensão.

-- Exemplo ETL simplificado em SQL: inserir novos snapshots
INSERT INTO fact_event_snapshot (
  entity_id, event_timestamp, event_type, status_before, status_after, amount, dim_date_key, dim_user_key, load_batch_id
)
SELECT
  e.entity_id,
  e.event_ts,
  e.event_type,
  e.status_before,
  e.status_after,
  e.amount,
  d.date_key,
  u.user_key,
  :batch_id
FROM staging_events e
LEFT JOIN dim_date d ON d.calendar_date = DATE(e.event_ts)
LEFT JOIN dim_user u ON u.user_id = e.user_id
WHERE e.processed_flag = 0; -- só novos eventos

Passo 5: Lidar com duplicados e idempotência

Evitar inserir duplicados é crítico. Use uma restrição única ou deduplicação antes de inserir. Uma opção é usar uma chave única lógica (entity_id + event_timestamp + event_type).

-- Criar índice único para evitar duplicados
ALTER TABLE fact_event_snapshot
ADD CONSTRAINT uq_event_snapshot UNIQUE (entity_id, event_timestamp, event_type);

-- Inserção defensiva (exemplo Postgres)
INSERT INTO fact_event_snapshot (...)
SELECT ...
ON CONFLICT (entity_id, event_timestamp, event_type) DO NOTHING;

Passo 6: Atualizações de atributos e Slowly Changing Dimensions

Se atributos relacionados com as dimensões mudam, trate como Slowly Changing Dimensions (SCD). A fact_snapshot deve referenciar a versão correcta da dimensão (surrogate key) para preservar histórico. No ETL, faça lookup da dimensão actual ou histórica conforme necessário.

Verificar o resultado

Verifique que cada evento relevante tem uma linha na fact:

  • Contar eventos na origem vs linhas na fact por período: SELECT COUNT(*) por intervalo.
  • Verificar unicidade: SELECT entity_id, event_timestamp, COUNT(*) HAVING COUNT(*) > 1.
  • Testar queries analíticas: tempo médio entre event_type = 'A' e event_type = 'B' por entity_id.
-- Exemplos de verificação
-- 1) Verificar total
SELECT COUNT(*) FROM staging_events WHERE processed_flag = 0;
SELECT COUNT(*) FROM fact_event_snapshot WHERE load_batch_id = :batch_id;

-- 2) Duplicados
SELECT entity_id, event_timestamp, event_type, COUNT(*)
FROM fact_event_snapshot
GROUP BY entity_id, event_timestamp, event_type
HAVING COUNT(*) > 1;

-- 3) Exemplo analítico: tempo entre eventos
SELECT f1.entity_id, EXTRACT(EPOCH FROM (f2.event_timestamp - f1.event_timestamp))/3600 AS hours_between
FROM fact_event_snapshot f1
JOIN fact_event_snapshot f2 ON f1.entity_id = f2.entity_id
WHERE f1.event_type = 'A' AND f2.event_type = 'B' AND f2.event_timestamp > f1.event_timestamp;

Conclusão

Uma Fact table 'Snapshot por Evento' oferece granularidade para analisar a evolução por acontecimentos sem perder histórico. Próximos passos: integrar com Power BI para visualizações de funnel e tempos, automatizar a rotina ETL e adicionar métricas de qualidade. Dica: comece com índices/constraints para idempotência e validação simples para evitar dados duplicados — qual é o primeiro evento crítico que vai capturar no seu caso?