Saltar al contenido

lección 4

Granularidad: la decisión más importante del modelado

Entiende por qué elegir el nivel de detalle correcto define el éxito o fracaso de todo tu warehouse.

50 min

Si tuviera que elegir UNA sola decisión de diseño que determina el éxito o fracaso de un Data Warehouse, sería la granularidad. La granularidad (o "grain" en inglés) es el nivel de detalle de cada fila de tu tabla de hechos. ¿Cada fila es una transacción individual? ¿Un resumen diario? ¿Un resumen mensual por producto? Esta decisión es IRREVERSIBLE una vez que tienes datos en producción, y afecta a TODAS las preguntas que podrás responder.

La analogía: piensa en una cámara de seguridad. Si graba a 30 fotogramas por segundo (alta granularidad), puedes ver exactamente qué pasó en cualquier momento. Si graba 1 foto por hora (baja granularidad), puedes ver quién estuvo, pero no qué hicieron exactamente. Una vez grabado a 1 foto/hora, no puedes "desgrabar" para recuperar lo que pasó entre las fotos. Lo mismo pasa con tu warehouse.

### La regla de oro de Kimball sobre granularidad

Ralph Kimball lo dijo de forma irrefutable: "siempre diseña al nivel de granularidad más atómico posible". ¿Por qué? Porque siempre puedes AGREGAR datos atómicos hacia arriba (sumar transacciones individuales para obtener totales mensuales), pero NUNCA puedes DESAGREGAR datos resumidos hacia abajo (de un total mensual no puedes volver a las transacciones individuales).

Lo que le diría a mi yo de hace 10 años: la primera vez que alguien me pidió "granularidad diaria es suficiente, no necesitamos cada transacción", le hice caso. Tres meses después, el equipo de fraude necesitaba ver transacciones individuales para detectar patrones. Tuvimos que reconstruir todo. Desde entonces, mi respuesta es siempre: guarda al nivel más atómico. Agregas después con CTEs o vistas materializadas. Es 100x más fácil sumar que descomponer.

### Tipos de granularidad

  • Transaccional: una fila por cada evento individual (cada línea de venta, cada clic, cada llamada) — la más atómica y flexible
  • Snapshot periódico: una fila por entidad por período (saldo de cada cuenta al cierre de cada día) — útil para estados acumulados
  • Snapshot acumulado: una fila por ciclo de vida de un proceso (un pedido desde creación hasta entrega) — mide duración de procesos
  • Agregada: resúmenes precalculados (ventas diarias por tienda) — sacrifica detalle por rendimiento

### Ejemplo: ¿cuál es el grain de fact_sales?

En nuestro esquema de e-commerce, necesitamos decidir: ¿una fila por PEDIDO o una fila por LÍNEA DE PEDIDO? Si un pedido tiene 3 productos, ¿es 1 fila o 3 filas? La respuesta correcta: una fila por línea de pedido (product_key + order_number). ¿Por qué? Porque si guardo 1 fila por pedido, NO puedo saber qué productos compraron juntos, ni calcular el ticket medio por categoría, ni analizar márgenes por producto individual.

1-- GRAIN: Una fila por línea de pedido (el nivel más atómico)
2-- Un pedido con 3 productos = 3 filas en fact_sales
3
4-- Ejemplo: pedido #ORD-5678 con 3 líneas
5-- | sales_key | order_number | product_key | quantity | net_amount |
6-- |-----------|-------------|-------------|----------|------------|
7-- | 1001 | ORD-5678 | 42 | 2 | 59.98 |
8-- | 1002 | ORD-5678 | 87 | 1 | 24.99 |
9-- | 1003 | ORD-5678 | 15 | 3 | 89.97 |
10
11-- Con este grain PUEDO calcular todo:
12-- Total por pedido: GROUP BY order_number
13-- Total por producto: GROUP BY product_key
14-- Total por día: GROUP BY date_key
15-- Total por categoría: JOIN dim_product, GROUP BY category
16-- Productos comprados juntos: self-join por order_number
17
18-- Si hubiera guardado 1 fila por pedido (amount=174.94),
19-- PERDERÍA toda esa flexibilidad para siempre.

El grain atómico (línea de pedido) permite responder cualquier pregunta. Un grain más grueso te limita.

### Declaración de granularidad: el documento más importante

Antes de crear cualquier fact table, escribe una frase que declare su granularidad. Esta frase es un contrato con el negocio. Ejemplos:

  • fact_sales: "Una fila por cada línea de artículo en cada pedido confirmado"
  • fact_page_views: "Una fila por cada página vista por cada sesión de usuario"
  • fact_daily_inventory: "Una fila por cada producto en cada almacén al cierre de cada día"
  • fact_subscription: "Una fila por cada suscripción desde creación hasta cancelación o renovación"
  • fact_call_center: "Una fila por cada llamada recibida en el call center"

Si no puedes escribir esa frase en una oración clara, no has terminado de pensar el diseño. Cada palabra importa: "confirmado" excluye pedidos cancelados, "por cada almacén" significa que un producto con stock en 3 almacenes genera 3 filas, "al cierre de cada día" define que es un snapshot diario.

### Granularidad y dimensiones: la conexión

La granularidad determina qué dimensiones puedes usar. Si tu grain es "una venta por día por tienda" (agregado), NO puedes tener dim_customer en esa fact table — porque un resumen diario incluye MUCHOS clientes. Solo puedes poner dimensiones que tengan UN valor por cada fila al nivel de granularidad declarado.

1-- CORRECTO: grain = "una línea de venta"
2-- Cada fila tiene exactamente UN date, UN customer, UN product
3fact_sales(date_key, customer_key, product_key, quantity, amount)
4
5-- INCORRECTO: grain = "ventas diarias por tienda"
6-- pero intentamos meter customer_key... ¿cuál cliente? ¡Hay muchos por día/tienda!
7fact_daily_store_sales(date_key, store_key, customer_key???, total_amount)
8-- Esto es un error de diseño: el grain no soporta esa dimensión
9
10-- CORRECTO: grain = "ventas diarias por tienda" (sin customer)
11fact_daily_store_sales(date_key, store_key, total_amount, num_transactions)

El grain dicta qué dimensiones pueden existir. Si no encaja, el grain es incorrecto o la dimensión sobra.

NUNCA mezcles granularidades en una misma fact table. Si tienes filas de transacciones individuales junto con filas de resúmenes diarios, las sumas van a ser INCORRECTAS (double-counting). Es el bug más silencioso y peligroso del Data Warehouse — los números "se ven bien" pero están mal. He visto a una empresa reportar el doble de revenue durante 2 meses antes de que alguien se diera cuenta.

### Tablas de agregación: rendimiento sin perder el atómico

Cuando fact_sales tiene 2.000 millones de filas, un GROUP BY por mes es lento aunque sea columnar. La solución: mantén la tabla atómica Y crea tablas de agregación precalculadas (agg_monthly_sales, agg_daily_category_sales). El analista consulta la agregada cuando puede, y baja a la atómica cuando necesita detalle. Herramientas de BI como Looker hacen esto automáticamente ("aggregate awareness").

Patrón que uso en todos mis proyectos: fact table atómica (la fuente de verdad) + 2-3 vistas materializadas/tablas agregadas para los dashboards más consultados. El ETL carga la atómica y luego refresca las agregaciones. Así el CEO tiene su dashboard rápido Y el analista puede hacer drill-down al detalle cuando algo se ve raro.

## ejercicios

[01]

Declarar la granularidad correcta

Para cada escenario de negocio, escribe la declaración de granularidad de la fact table. Recuerda: debe ser una frase clara que defina exactamente qué representa CADA FILA. 💡 Cómo saber si tus cinco declaraciones valen (antes de abrir la solución): cada frase tiene que empezar por «una fila por cada» y acabar en un SUSTANTIVO concreto y contable. Si acabas en «datos» o «información», no vale. Cuatro de los cinco son eventos (viaje, visita, reproducción, artículo devuelto). Uno NO lo es — el saldo no ocurre, se fotografía.

Cargando editor...
[02]

Detectar errores de granularidad

Este esquema tiene un error de granularidad mezclada. Identifícalo y propón la corrección. 💡 Cómo saber si tu análisis vale: el error está en las MÉTRICAS, no en las claves. Haz esta prueba mental: si esta tienda tuvo 500 líneas hoy, ¿cuántas veces aparece el número «total del día» en la tabla? 500. ¿Y qué pasa si lo sumas? Sale 500 veces el total real. Tu solución vale si acabas con DOS tablas, cada una con una frase de grano distinta.

Cargando editor...
[03]

Verificar si una pregunta es respondible con el grain

Dado un fact_monthly_revenue con grain "una fila por mes por categoría de producto", determina cuáles de estas preguntas se pueden responder y cuáles NO. 💡 Cómo saber si tus respuestas valen: te tienen que salir TRES SÍ y DOS NO. Y para cada NO, di qué te falta exactamente: en una falta DETALLE (el grano es mensual, no hay pedidos), en la otra falta una DIMENSIÓN (no hay cliente). Regla: hacia arriba siempre se puede (meses → trimestre); hacia abajo, nunca.

Cargando editor...
[04]

Elegir el grain correcto para un gimnasio

Una cadena de gimnasios quiere analizar asistencia, clases y uso de máquinas. Diseña 2 fact tables con grains diferentes y explica qué preguntas responde cada una. 💡 Cómo saber si tus dos tablas valen: escribe DEBAJO de cada tabla tres preguntas que ESA tabla responde y la otra NO. Si te cuesta encontrar tres para una de las dos, esa tabla no hace falta. Dos comprobaciones más: (1) la snapshot tiene que tener PRIMARY KEY (date_key, gym_key) — esa clave ES la declaración del grano. (2) Ninguna de las dos puede tener un % ni una media como métrica — guarda las partes y divide al consultar.

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