lección 7
Métricas y KPIs: diseñar facts que respondan preguntas de negocio
Métricas aditivas, semi-aditivas y no aditivas. Fact tables transaccionales, snapshot y accumulating. El arte de que el negocio se auto-sirva.
⏱ 55 min
Una fact table técnicamente correcta puede ser completamente INÚTIL para el negocio. He visto warehouses impecables donde los analistas seguían pidiendo al ingeniero que les sacara cada número porque las métricas no estaban diseñadas para responder las preguntas reales. El objetivo de esta lección es que tus fact tables estén diseñadas para que el equipo de negocio pueda AUTOSERVIRSE: hacer drag-and-drop en Tableau o escribir un GROUP BY simple y obtener la respuesta correcta.
La clave está en entender que no todas las métricas se comportan igual. Puedes sumar revenue de dos tiendas para obtener el revenue total — pero NO puedes sumar la temperatura de dos ciudades para obtener la "temperatura total". Esa diferencia (aditiva vs no-aditiva) es fundamental para diseñar fact tables que no produzcan resultados absurdos cuando alguien hace GROUP BY.
### Métricas aditivas: las que puedes sumar en TODAS las dimensiones
Una métrica aditiva se puede sumar a través de CUALQUIER dimensión y el resultado tiene sentido. Revenue es aditiva: puedes sumar por producto, por día, por región, por cualquier combinación, y el total es correcto. Son las métricas ideales para un warehouse porque funcionan con cualquier GROUP BY sin restricciones.
- revenue / amount: sumar ventas de varios días = total correcto
- quantity: sumar unidades vendidas en varias tiendas = total correcto
- cost: sumar costes de varios productos = coste total correcto
- discount_amount: sumar descuentos = total de descuentos dado
- clicks: sumar clics de varias campañas = total de clics
- duration_seconds: sumar duraciones = tiempo total invertido
### Métricas semi-aditivas: sumables solo en ALGUNAS dimensiones
Aquí es donde muchos ingenieros se equivocan. Una métrica semi-aditiva se puede sumar a través de algunas dimensiones pero NO de todas. El ejemplo clásico: el saldo de una cuenta bancaria. Puedes sumar saldos de varias CUENTAS para obtener el total del banco (aditiva por dimensión cuenta). Pero NO puedes sumar saldos de varios DÍAS para la misma cuenta — eso no significa nada. Para el tiempo, necesitas PROMEDIAR o tomar el último valor.
1-- SEMI-ADITIVA: balance de cuenta (aditivo por cuenta, NO por tiempo)23-- ✅ CORRECTO: sumar saldos de varias cuentas (mismo día)4-- "¿Cuál es el saldo total del banco al cierre del 15 de marzo?"5SELECT SUM(balance) AS total_bank_balance6FROM fact_daily_balance7WHERE date_key = 20240315;8-- Resultado: suma de todos los saldos = total del banco. Correcto.910-- ❌ INCORRECTO: sumar saldos de varios días (misma cuenta)11-- "¿Cuál es el saldo total de la cuenta #123 en Q1?"12SELECT SUM(balance) AS total_balance_q1 -- ¡¡¡ESTO NO TIENE SENTIDO!!!13FROM fact_daily_balance14WHERE account_key = 123 AND date_key BETWEEN 20240101 AND 20240331;15-- Resultado: suma de 90 saldos diarios = un número absurdo1617-- ✅ CORRECTO: usar AVG o LAST para la dimensión tiempo18SELECT AVG(balance) AS avg_balance_q119FROM fact_daily_balance20WHERE account_key = 123 AND date_key BETWEEN 20240101 AND 20240331;2122-- O el último valor del período:23SELECT balance AS end_of_quarter_balance24FROM fact_daily_balance25WHERE account_key = 123 AND date_key = 20240331;
El saldo es semi-aditivo: sumable entre cuentas, pero para tiempo necesitas AVG o último valor.
### Métricas no aditivas: nunca se pueden sumar
Las métricas no aditivas NUNCA se pueden sumar en ninguna dimensión. Son ratios, porcentajes, promedios almacenados. Sumar dos porcentajes no da el porcentaje total — sumar "30% de margen" + "40% de margen" NO da "70% de margen". Para estas métricas, necesitas almacenar los componentes por separado y calcular el ratio en la query.
1-- NO ADITIVAS: ratios y porcentajes23-- ❌ MAL: guardar el margen como porcentaje en la fact table4-- fact_sales: margin_pct = 30%5-- Si sumas margin_pct de 100 filas, obtienes 3000% — absurdo67-- ✅ BIEN: guardar los componentes (revenue y cost) por separado8-- El ratio se CALCULA en la query, no se almacena9SELECT10 p.category,11 SUM(f.net_amount) AS revenue,12 SUM(f.cost) AS total_cost,13 -- El margen se CALCULA, no se suma:14 (SUM(f.net_amount) - SUM(f.cost)) / NULLIF(SUM(f.net_amount), 0) * 10015 AS margin_pct16FROM fact_sales f17JOIN dim_product p ON f.product_key = p.product_key18GROUP BY p.category;1920-- Otros ejemplos de no-aditivas que NO debes guardar pre-calculadas:21-- conversion_rate = conversions / visits (guarda ambas por separado)22-- avg_order_value = total_revenue / num_orders (guarda ambas)23-- bounce_rate = bounces / sessions (guarda ambas)
Regla: nunca guardes ratios precalculados en la fact table. Guarda los componentes y calcula en la query.
El test mental que uso para clasificar métricas: "¿tiene sentido sumar este número para dos tiendas diferentes?" Si sí → aditiva. "¿Tiene sentido sumar este número para dos días diferentes (misma entidad)?" Si no → semi-aditiva. "¿Tiene sentido sumar este número en ALGUNA dimensión?" Si nunca → no aditiva. Diseña tu fact table en consecuencia: aditivas se guardan directo, semi-aditivas se documentan, no-aditivas se descomponen en componentes.
### Tipos de fact tables según el proceso
No todos los procesos de negocio encajan en el mismo tipo de fact table. Kimball definió tres tipos que cubren prácticamente cualquier escenario:
- Transaccional: una fila por evento discreto (cada venta, cada clic, cada llamada). Es la más común y la más granular.
- Snapshot periódico: una fila por entidad por período (saldo de cada cuenta al cierre de cada día, inventario diario). Captura el estado en un momento.
- Accumulating snapshot: una fila por ciclo de vida completo de un proceso (un pedido desde creación hasta entrega). Las columnas de fecha se van llenando conforme avanza el proceso.
### Fact table transaccional
La más intuitiva: cada fila es un evento que OCURRIÓ en un momento específico. Una venta, un clic, una llamada, un envío. Una vez insertada la fila, normalmente NO se modifica. Es la más granular y flexible: siempre puedes agregar hacia arriba. La desventaja: con billones de eventos, puede ser enorme.
### Fact table de snapshot periódico
Captura el ESTADO de algo en intervalos regulares. Ejemplo: el stock de cada producto en cada almacén al final de cada día. No puedes representar esto con una fact transaccional fácilmente (tendrías que sumar todos los movimientos históricos). El snapshot simplemente dice "el 15 de marzo a las 23:59, el producto X tenía 47 unidades en el almacén Y".
1-- SNAPSHOT PERIÓDICO: inventario diario2CREATE TABLE fact_daily_inventory (3 date_key INTEGER NOT NULL,4 product_key INTEGER NOT NULL,5 warehouse_key INTEGER NOT NULL,6 -- Métricas del snapshot7 quantity_on_hand INTEGER, -- stock disponible8 quantity_reserved INTEGER, -- reservado para pedidos9 quantity_in_transit INTEGER, -- en camino10 reorder_point INTEGER, -- punto de reorden11 days_of_supply DECIMAL(5,1), -- días que dura el stock actual12 PRIMARY KEY (date_key, product_key, warehouse_key)13);1415-- Este snapshot crece date × products × warehouses por DÍA16-- Con 10K productos y 5 almacenes: 50K filas por día = 18M al año17-- Semi-aditiva: puedes sumar quantity_on_hand entre almacenes,18-- pero NO entre días (sumar stock de lunes + martes no tiene sentido)
El snapshot periódico responde "¿cómo estaban las cosas en ese momento?" — esencial para inventario, saldos, pipelines.
### Fact table accumulating snapshot
La menos común pero la más interesante: rastrea el CICLO DE VIDA completo de un proceso con MÚLTIPLES hitos. Cada fila es un proceso (un pedido, un ticket de soporte, una solicitud de préstamo) y tiene múltiples columnas de fecha que se van llenando conforme el proceso avanza. Es MUTABLE — las filas se actualizan cuando el proceso avanza de fase.
1-- ACCUMULATING SNAPSHOT: ciclo de vida de un pedido2CREATE TABLE fact_order_lifecycle (3 order_key BIGINT PRIMARY KEY,4 order_number VARCHAR(20),5 customer_key INTEGER,6 -- Múltiples date_keys (uno por hito)7 order_date_key INTEGER, -- cuándo se creó8 payment_date_key INTEGER, -- cuándo se pagó (NULL si pendiente)9 ship_date_key INTEGER, -- cuándo se envió (NULL si no enviado)10 delivery_date_key INTEGER, -- cuándo se entregó (NULL si no entregado)11 -- Métricas12 order_amount DECIMAL(12,2),13 -- Métricas CALCULADAS de duración (se actualizan al completar cada hito)14 days_to_payment INTEGER, -- payment_date - order_date15 days_to_ship INTEGER, -- ship_date - payment_date16 days_to_delivery INTEGER, -- delivery_date - ship_date17 total_cycle_days INTEGER -- delivery_date - order_date18);1920-- Esta tabla se ACTUALIZA conforme avanza el pedido:21-- Día 1: INSERT con order_date, todo lo demás NULL22-- Día 2: UPDATE payment_date_key, days_to_payment23-- Día 4: UPDATE ship_date_key, days_to_ship24-- Día 7: UPDATE delivery_date_key, days_to_delivery, total_cycle_days2526-- Query: "¿Cuál es el tiempo medio de entrega por región?"27SELECT c.country, AVG(f.total_cycle_days) AS avg_delivery_days28FROM fact_order_lifecycle f29JOIN dim_customer c ON f.customer_key = c.customer_key30WHERE f.delivery_date_key IS NOT NULL -- solo pedidos completados31GROUP BY c.country;
El accumulating snapshot mide CUÁNTO TARDA cada fase. Perfecto para SLAs y cuellos de botella.
El accumulating snapshot es MUTABLE (sus filas se actualizan). Esto rompe la regla general de que las fact tables son "append-only". Tenlo en cuenta para tu ETL: necesitas MERGE/UPSERT, no simple INSERT. Y el SCD de las dimensiones debe manejarse con cuidado — el customer_key se asigna al crear el pedido y NO cambia aunque el cliente se mude después.
## ejercicios
Clasificar métricas por aditividad
Para cada métrica, clasifícala como aditiva (A), semi-aditiva (SA) o no-aditiva (NA) y justifica. 💡 Cómo saber si tu clasificación vale: te tienen que salir 2 aditivas, 3 semi-aditivas y 3 no-aditivas. La prueba: «¿puedo sumar esto entre tiendas?» + «¿puedo sumarlo entre días?». sí+sí = A, sí+no = SA, no+no = NA. La trampa es temperature: no es un ratio, y aun así no se suma nunca.
Elegir el tipo de fact table correcto
Para cada escenario de negocio, elige el tipo de fact table más adecuado: Transaccional, Snapshot periódico o Accumulating snapshot. 💡 Cómo saber si tus respuestas valen: salen 2 transaccionales, 1 snapshot periódico y 2 accumulating. La pregunta que los separa: ¿mido un EVENTO (transaccional), un ESTADO en una fecha (snapshot) o CUÁNTO TARDA un proceso (accumulating)?
Descomponer métricas no-aditivas
El dashboard muestra "tasa de abandono del carrito" por día. El equipo de BI lo calculó como métrica pre-calculada. Rediseña la fact table para que la métrica se calcule correctamente al agregar por semana o mes. 💡 Cómo saber si tu rediseño vale: (1) En tu tabla NO puede quedar ninguna columna que acabe en _rate, _pct o _avg — si queda una, no has descompuesto nada. (2) Agrupa tu consulta por mes y luego por trimestre: si el trimestre no coincide con lo que darían sus tres meses juntos, tu ratio se está calculando antes de sumar.
Diseñar un accumulating snapshot para préstamos
Un banco quiere medir cuánto tarda un préstamo desde la solicitud hasta el desembolso, pasando por: solicitud → revisión → aprobación → firma → desembolso. Diseña la fact table. 💡 Cómo saber si tu tabla vale: cuenta los date_key — tienen que ser CINCO hitos más uno (el rechazo). Sin rejection_date_key no puedes medir la tasa de aprobación. Y escribe la consulta «¿qué préstamos están parados y desde cuándo?» — si no puedes responderla, te falta current_status o los hitos intermedios.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...