Como criar uma Fact table de tipo 'Snapshot por Evento' em Modelação de Dados (Kimball)
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?