Rolling 12 meses en DAX: la solución de dos calendarios
TL;DR
- Rolling 12 meses dinámico requiere dos calendarios: uno para el slicer, otro para el eje X
- La relación entre calendarios debe ser inactiva (activarla con USERELATIONSHIP)
- Patrón complejo: pregunta al cliente si realmente lo necesita antes de implementar
- A veces un filtro de rango simple es suficiente
El requisito
Parece simple: un gráfico de líneas con los últimos 12 meses, y un segmentador para elegir el mes final.
Seleccionas Diciembre 2024, y el gráfico muestra Enero 2024 - Diciembre 2024. Seleccionas Marzo 2025, muestra Abril 2024 - Marzo 2025.
Fácil, ¿no?
No.
El problema
Si usas un solo calendario, tienes un conflicto de intereses:
- El segmentador filtra por mes (Diciembre 2024)
- El eje X del gráfico necesita mostrar 12 meses diferentes
Con un solo calendario, cuando filtras por Diciembre, el gráfico solo tiene un punto. El filtro afecta a todo. Es como intentar usar la misma variable para dos cosas distintas: no funciona.
La solución: Dos calendarios
La arquitectura que necesitas es:
- Calendar: Conectado a la tabla de hechos, alimenta el segmentador
- Calendar_Display: Alimenta el eje X del gráfico, NO conecta a la tabla de hechos
La clave está en crear una relación INACTIVA entre ambos calendarios. La despiertas solo cuando la necesitas, con USERELATIONSHIP.
El patrón DAX
Ventas Rolling 12 =
VAR FechaSeleccionada =
CALCULATE(
MAX(Calendar[Date]),
ALLEXCEPT(Calendar, Calendar[Year], Calendar[Month])
)
VAR Ultimos12Meses =
DATESINPERIOD(
Calendar_Display[Date],
FechaSeleccionada,
-12,
MONTH
)
VAR FechaActualEje = SELECTEDVALUE(Calendar_Display[Date])
VAR EstaEnRango =
FechaActualEje >= MINX(Ultimos12Meses, [Date]) &&
FechaActualEje <= MAXX(Ultimos12Meses, [Date])
RETURN
IF(
EstaEnRango,
CALCULATE(
[Ventas],
REMOVEFILTERS(Calendar),
KEEPFILTERS(Ultimos12Meses),
USERELATIONSHIP(Calendar[Date], Calendar_Display[Date])
),
BLANK()
)
Qué hace cada parte
- FechaSeleccionada: Captura lo que el usuario eligió en el segmentador
- Ultimos12Meses: Calcula el rango de 12 meses hacia atrás desde esa fecha
- REMOVEFILTERS: Limpia el filtro del segmentador (si no, solo tendrías 1 mes en el gráfico)
- KEEPFILTERS: Mantiene el rango de 12 meses que acabamos de calcular
- USERELATIONSHIP: Activa la relación inactiva entre calendarios
- EstaEnRango: Evita mostrar datos fuera del rango (si no, te salen puntos fantasma)
Por qué la relación tiene que ser inactiva
Si la activas permanentemente, creas ambigüedad en el modelo. Power BI ve dos caminos posibles entre el calendario y los hechos, y no sabe cuál seguir.
Por eso la dejas inactiva por defecto y la activas solo en las medidas específicas que la necesitan. Es como un interruptor: encendido solo cuando hace falta.
Antes de implementarlo
Este patrón es complejo y difícil de mantener. Antes de meterte en este jardín, pregunta al cliente si realmente necesita un rolling dinámico o si le vale con un filtro simple de rango de fechas.
A veces la respuesta es “ah, pues sí, con un filtro de fecha me vale”. Y te acabas de ahorrar tres horas de debug.
¿Te ha servido? Tengo más patrones DAX oscuros en el cajón.
¿Empezando con DAX? Lee primero Qué es DAX en Power BI: Guía para principiantes.
¿Problemas con relaciones inactivas? Lee Power BI desactivó tu relación y no te avisó para entender USERELATIONSHIP a fondo.
También te puede interesar
Medidas Rotativas en DAX: Cuando tus datos no se están quietos
Cómo manejar productos que cambian de categoría con el tiempo en DAX.
Qué es DAX en Power BI: Guía práctica para principiantes (con ejemplos)
Aprende DAX desde cero: qué es, para qué sirve, diferencia con Power Query, las 5 funciones esenciales y errores comunes. Con ejemplos de código.
Power BI desactivó tu relación y no te avisó
Guía completa sobre relaciones inactivas en Power BI: por qué aparecen, cómo detectarlas, USERELATIONSHIP, role-playing dimensions y patrones avanzados.