Serie: Power BI para Gestión de Programas Cloud
Prerrequisito: Artículo 1 — Power Query: conexión y transformaciones del Excel
Introducción
En el artículo anterior conectamos el Excel al Power BI y preparamos los datos en Power Query. Ahora toca tomar la decisión más importante antes de escribir una sola medida DAX: ¿cómo estructuramos el modelo de datos?
Esta decisión afecta al rendimiento, a la legibilidad del DAX, a la facilidad de mantenimiento y a cómo escala el modelo si el programa crece. No hay una respuesta única correcta — depende del contexto. En este artículo presento los dos enfoques principales, sus ventajas e inconvenientes, y los criterios para elegir uno u otro.
Los dos enfoques
Opción A: Tabla plana (Flat Table)
Todos los atributos del servicio en una sola tabla. Cada fila es un servicio; cada columna, un atributo.
AllServices
├── service_name
├── WAVE
├── MIGRATION SCOPE
├── MIGRATION STRATEGY
├── Environment
├── Infra_type
├── Criticality
├── PROVEEDOR Implementation Status
├── Fecha esperada de migración
├── Fecha de migración/monitorización
└── … (31 columnas)
El modelo queda así: dos tablas — AllServices y Calendario — con una (o varias) relaciones entre ellas.
Opción B: Star Schema (Esquema en estrella)
Los atributos categóricos se separan en tablas de dimensiones independientes. La tabla central (FactServicios) contiene solo hechos medibles y claves foráneas.
dim_Wave dim_Scope dim_Strategy
│ │ │
└─────────────────┴──────────────────┘
│
FactServicios Calendario
(claves + fechas │
+ métricas) ────┘
Las tablas de dimensiones típicas para nuestro modelo serían:
| Tabla dimensión | Columnas clave |
|---|---|
dim_Wave | WaveID, Wave, OrdenWave |
dim_Scope | ScopeID, MigrationScope, TipoScope (ThirdParty / InHouse) |
dim_Strategy | StrategyID, MigrationStrategy |
dim_Environment | EnvironmentID, Environment |
dim_Criticality | CriticalityID, Criticality, NivelPrioridad |
dim_Proveedor | ProveedorID, ProveedorNombre |
Calendario | Date, Año, Trimestre, Mes, Semana… |
Comparativa directa
| Criterio | Tabla plana | Star Schema |
|---|---|---|
| Complejidad inicial | Baja — una tabla, listo | Alta — hay que crear y relacionar dimensiones |
| Rendimiento | Bueno hasta ~100k filas | Óptimo a cualquier escala; VertiPaq comprime mejor columnas de baja cardinalidad en dims separadas |
| Legibilidad del DAX | Medidas más largas con filtros explícitos | Medidas más limpias; los filtros viajan por las relaciones automáticamente |
| Mantenimiento | Fácil si el Excel no cambia de estructura | Más robusto ante cambios: modificar una dimensión no afecta a la tabla de hechos |
| Valores duplicados | Sí — «ZEUS (ThirdParty)» se repite en 8 filas | No — cada scope aparece una sola vez en dim_Scope |
| Filtros cruzados | Requieren CROSSFILTER o BOTH en relaciones | Fluyen naturalmente de dim → fact |
| Curva de aprendizaje | Menor | Mayor — hay que entender cardinalidad y dirección de filtros |
| Fuente única (Excel) | Ideal | Requiere generar las dims en Power Query o DAX |
¿Cuándo usar cada uno?
Usa tabla plana cuando:
- El origen de datos es un único Excel que controlas tú directamente.
- El volumen de datos es menor de 100.000 filas.
- El equipo que mantiene el modelo tiene experiencia limitada en Power BI.
- El programa tiene fecha de fin definida y el modelo no necesita escalar indefinidamente.
- La velocidad de desarrollo importa más que la arquitectura perfecta.
Usa star schema cuando:
- Los datos vienen de múltiples fuentes (Excel + Jira + ServiceNow + Azure Cost Management).
- El volumen supera las 100.000 filas o crece de forma continua.
- Varios equipos van a construir medidas DAX sobre el mismo modelo.
- Las dimensiones cambian con frecuencia (nuevos scopes, nuevas waves) y quieres gestionar esos cambios en un único sitio.
- El rendimiento de las consultas empieza a ser perceptible.
El modelo de esta serie
En este programa trabajamos con un Excel de control único, ~500 servicios, y un equipo técnico pequeño. La tabla plana es la elección correcta por pragmatismo: nos permite ir directo a construir los dashboards sin invertir tiempo en arquitectura.
Aun así, introducimos un elemento del star schema que sí vale la pena en cualquier caso: la tabla Calendario separada. Esta tabla es obligatoria para las funciones de Time Intelligence de DAX (DATEADD, TOTALYTD, acumulados…) y no tiene coste de complejidad significativo.
El modelo final de esta serie tiene esta estructura:
Calendario ──(activa)──────► AllServices[Fecha esperada de migración]
──(inactiva)────► AllServices[Fecha de migración/monitorización]
──(inactiva)────► AllServices[Fecha de decomisionado]
──(inactiva)────► AllServices[Migration window]
Las relaciones inactivas se activan dentro de cada medida DAX con USERELATIONSHIP(). Este patrón — una tabla de fechas con múltiples roles — es el más habitual en modelos de seguimiento de programas y lo veremos en detalle en el artículo del Burn Up.
Nota sobre escalabilidad: Si en el futuro el programa incorpora datos de coste (AWS Cost Explorer, Azure Billing) o de gestión de incidencias (Jira, ServiceNow), ese sería el momento natural de migrar a star schema. La tabla
AllServicesse convertiría enFactServicios, y las columnas categóricas se extraerían a dimensiones. La migración es mecánica si el DAX está bien escrito.
Crear la tabla Calendario en DAX
Si no la creaste en el Artículo 1, aquí está la definición completa. En Power BI Desktop: Home → New Table y pega este código:
dax
Calendario =
ADDCOLUMNS(
CALENDAR(DATE(2025, 1, 1), DATE(2026, 12, 31)),
"Año", YEAR([Date]),
"Trimestre", "Q" & QUARTER([Date]),
"Mes Num", MONTH([Date]),
"Mes Nombre", FORMAT([Date], "MMMM"),
"Mes Corto", FORMAT([Date], "MMM"),
"Semana", WEEKNUM([Date], 2),
"Día Semana", WEEKDAY([Date], 2),
"Día Nombre", FORMAT([Date], "dddd"),
"AñoMes", FORMAT([Date], "YYYY-MM"),
"AñoTrimestre", FORMAT([Date], "YYYY") & "-" & "Q" & QUARTER([Date])
)
Una vez creada, ve a la vista de Modelo → clic derecho en Calendario → Mark as Date Table → selecciona la columna Date. Esto es obligatorio para que las funciones de Time Intelligence funcionen correctamente.
Por qué CALENDAR y no CALENDARAUTO
CALENDARAUTO genera automáticamente el rango de fechas basándose en las columnas de fecha del modelo. Es cómoda pero frágil: si una fecha errónea llega en el Excel (por ejemplo, un valor 01/01/1900 por un error de formato), la tabla Calendario se expandirá hasta esa fecha y las funciones de acumulado dejarán de funcionar bien. CALENDAR con fechas explícitas es más predecible y segura en modelos alimentados por Excel.
Establecer las relaciones
Con la tabla Calendario creada y marcada, ve a la vista de Modelo y arrastra:
1. Relación activa (la que Power BI usa por defecto):
Calendario[Date]→AllServices[Fecha esperada de migración]- Cardinalidad: Uno a muchos (1:*)
- Dirección de filtro: Single (Calendario filtra AllServices)
2. Relaciones inactivas (se activarán en DAX con USERELATIONSHIP):
Calendario[Date]→AllServices[Fecha de migración/monitorización]Calendario[Date]→AllServices[Fecha de decomisionado]Calendario[Date]→AllServices[Migration window]
Power BI no permite más de una relación activa entre el mismo par de tablas. Las relaciones inactivas existen en el modelo pero no filtran datos por defecto — solo se activan cuando una medida DAX las invoca explícitamente con
USERELATIONSHIP().
Dirección de filtro: Single vs. Both
Mantén todas las relaciones en dirección Single (Calendario → AllServices). La dirección Both (bidireccional) puede parecer conveniente pero introduce ambigüedad en el filtrado y es una fuente habitual de resultados incorrectos en modelos con múltiples relaciones. En un modelo de tabla plana con una sola tabla de hechos no hay ningún escenario que requiera filtrado bidireccional.
Verificación del modelo
Antes de pasar a escribir medidas DAX, comprueba en la vista de Modelo que:
- La tabla
Calendariotiene el icono de calendario (📅) en la columnaDate— indica que está marcada como Date Table. - Hay exactamente una línea continua (relación activa) y tres líneas discontinuas (relaciones inactivas) entre
CalendarioyAllServices. - Todas las relaciones son de cardinalidad 1:* (uno en Calendario, muchos en AllServices).
- No hay relaciones entre
AllServicesy sí misma.
Próximo artículo
Con el modelo de datos listo y las relaciones establecidas, en el Artículo 3 construiremos las medidas DAX fundamentales del programa:
# Total Elementos— base de todos los ratios# Migrados— conteo real de servicios en estado DONE# Por Migrar— lo que queda% Completado— ratio de progreso# Planificado Acumulado— línea planificada del Burn Up# Real Acumulado— línea real del Burn Up# Fecha Estimada Fin— proyección lineal hacia el fin de contrato
Cada medida vendrá acompañada del código DAX y la explicación de por qué está escrita así — incluyendo los patrones de CALCULATE, USERELATIONSHIP y FILTER que aparecen una y otra vez en este tipo de modelos.
Recursos del artículo: Excel mock (All_Services_AWS_MOCK.xlsx) disponible para descarga. Los datos son ficticios y no contienen información sensible.
-
Modelo de Datos — Tabla Plana vs. Star Schema
Serie: Power BI para Gestión de Programas CloudPrerrequisito: Artículo 1 — Power Query: conexión y…
-
Cómo estructurar el Excel y prepararlo en Power Query
Introducción Antes de construir cualquier visual en Power BI, hay que resolver una pregunta fundamental:…
-
Socratic Coaching for Project & Program Managers
The original principle holds: choose questions with the highest Return on Questions (ROQ)— questions that…

Deja una respuesta
Lo siento, debes estar conectado para publicar un comentario.