Saltar al contenido

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)
2
3-- ✅ 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_balance
6FROM fact_daily_balance
7WHERE date_key = 20240315;
8-- Resultado: suma de todos los saldos = total del banco. Correcto.
9
10-- ❌ 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_balance
14WHERE account_key = 123 AND date_key BETWEEN 20240101 AND 20240331;
15-- Resultado: suma de 90 saldos diarios = un número absurdo
16
17-- ✅ CORRECTO: usar AVG o LAST para la dimensión tiempo
18SELECT AVG(balance) AS avg_balance_q1
19FROM fact_daily_balance
20WHERE account_key = 123 AND date_key BETWEEN 20240101 AND 20240331;
21
22-- O el último valor del período:
23SELECT balance AS end_of_quarter_balance
24FROM fact_daily_balance
25WHERE 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 porcentajes
2
3-- ❌ MAL: guardar el margen como porcentaje en la fact table
4-- fact_sales: margin_pct = 30%
5-- Si sumas margin_pct de 100 filas, obtienes 3000% — absurdo
6
7-- ✅ BIEN: guardar los componentes (revenue y cost) por separado
8-- El ratio se CALCULA en la query, no se almacena
9SELECT
10 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) * 100
15 AS margin_pct
16FROM fact_sales f
17JOIN dim_product p ON f.product_key = p.product_key
18GROUP BY p.category;
19
20-- 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 diario
2CREATE 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 snapshot
7 quantity_on_hand INTEGER, -- stock disponible
8 quantity_reserved INTEGER, -- reservado para pedidos
9 quantity_in_transit INTEGER, -- en camino
10 reorder_point INTEGER, -- punto de reorden
11 days_of_supply DECIMAL(5,1), -- días que dura el stock actual
12 PRIMARY KEY (date_key, product_key, warehouse_key)
13);
14
15-- Este snapshot crece date × products × warehouses por DÍA
16-- Con 10K productos y 5 almacenes: 50K filas por día = 18M al año
17-- 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 pedido
2CREATE 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étricas
12 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_date
15 days_to_ship INTEGER, -- ship_date - payment_date
16 days_to_delivery INTEGER, -- delivery_date - ship_date
17 total_cycle_days INTEGER -- delivery_date - order_date
18);
19
20-- Esta tabla se ACTUALIZA conforme avanza el pedido:
21-- Día 1: INSERT con order_date, todo lo demás NULL
22-- Día 2: UPDATE payment_date_key, days_to_payment
23-- Día 4: UPDATE ship_date_key, days_to_ship
24-- Día 7: UPDATE delivery_date_key, days_to_delivery, total_cycle_days
25
26-- Query: "¿Cuál es el tiempo medio de entrega por región?"
27SELECT c.country, AVG(f.total_cycle_days) AS avg_delivery_days
28FROM fact_order_lifecycle f
29JOIN dim_customer c ON f.customer_key = c.customer_key
30WHERE f.delivery_date_key IS NOT NULL -- solo pedidos completados
31GROUP 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

[01]

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.

Cargando editor...
[02]

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)?

Cargando editor...
[03]

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.

Cargando editor...
[04]

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.

Cargando editor...

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...