Cómo crear una Fact table de tipo 'Snapshot por Evento' en Modelado de Datos (Kimball)
Este tutorial muestra cómo crear una Fact table de tipo 'Snapshot por Evento' en Modelado de Datos (Kimball): una tabla de hechos que registra el estado de una entidad cada vez que ocurre un evento relevante. Es útil para analizar la evolución por evento (p. ej.: cambios de estado de pedidos) sin confligir con snapshots periódicos ni con accumulating snapshots.
Prerequisitos
- Conocimientos básicos de Modelado Dimensional (Fact/DIMensions).
- Acceso a una base de datos SQL (p. ej.: SQL Server, PostgreSQL).
- Datos de origen con eventos (p. ej.: logs de estado de pedido con timestamp).
Paso 1: Entender cuándo usar un Snapshot por Evento
Un Snapshot por Evento graba el estado de la entidad cada vez que ocurre un evento que interesa para el análisis (p. ej.: cambio de estado, registro de pago). Úselo cuando necesite el historial por evento, sin perder el contexto temporal. Evita la pérdida de granularidad que ocurriría con snapshots periódicos y permite análisis de funnel y lead time entre eventos.
Paso 2: Definir la granularidad y las claves
Decida la granularidad: generalmente una fila por (entity_id, event_id/timestamp). Defina claves:
- Surrogate key de la fact (fact_snapshot_id) — identidad de la tabla de hechos.
- Natural keys: entity_id y event_timestamp (o event_id si existe).
- Foreign keys hacia dimensiones conformadas (p. ej.: 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
);
Paso 3: Mapear atributos e identificar dimensiones
Elija qué atributos van a la fact (medidas y atributos degenerate) y cuáles van a dimensiones. Medidas: valores que se agregan (amount, quantity). Atributos de contexto que se repiten y tienen baja cardinalidad deben ir a dimensiones (status, event_type puede ser una dimensión pequeña o string directo en la fact si es útil para rendimiento).
Paso 4: Extracción y transformación (ETL) — lógica de captura por evento
Implemente la lógica que detecta nuevos eventos y crea un snapshot por evento. Normalmente: leer source_events, para cada evento construir un registro con estado antes/después y enlazar claves de dimensión.
-- 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
Paso 5: Manejar duplicados e idempotencia
Evitar insertar duplicados es crítico. Use una restricción única o deduplicación antes de insertar. Una opción es usar una clave ú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;
Paso 6: Actualizaciones de atributos y Slowly Changing Dimensions
Si los atributos relacionados con las dimensiones cambian, trátelos como Slowly Changing Dimensions (SCD). La fact_snapshot debe referenciar la versión correcta de la dimensión (surrogate key) para preservar el historial. En el ETL, haga lookup de la dimensión actual o histórica según sea necesario.
Verificar el resultado
Verifique que cada evento relevante tenga una fila en la fact:
- Contar eventos en la fuente vs filas en la fact por período: SELECT COUNT(*) por intervalo.
- Verificar unicidad: SELECT entity_id, event_timestamp, COUNT(*) HAVING COUNT(*) > 1.
- Probar queries analíticas: tiempo medio entre event_type = 'A' y 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;
Conclusión
Una Fact table 'Snapshot por Evento' ofrece granularidad para analizar la evolución por acontecimientos sin perder historial. Próximos pasos: integrar con Power BI para visualizaciones de funnel y tiempos, automatizar la rutina ETL y añadir métricas de calidad. Consejo: empiece con índices/constraints para idempotencia y validación simple para evitar datos duplicados — ¿cuál es el primer evento crítico que va a capturar en su caso?