RANKX y empates: cómo controlar el criterio de desempate en DAX
En el desarrollo de informes profesionales de Power BI, la función RANKX es una de las más utilizadas y, paradójicamente, una de las peor comprendidas. No es raro encontrar informes donde los rankings muestran valores duplicados inesperados o saltos en la numeración que confunden al usuario final. Como consultores, nuestra responsabilidad no es solo entregar un número, sino garantizar que ese número sea determinista y responda a una lógica de negocio coherente.
El problema del ranking en DAX surge casi siempre en escenarios de empate. En el sector retail, por ejemplo, es frecuente que dos productos tengan exactamente la misma cifra de ventas en un periodo concreto. Si no definimos un criterio de desempate, el motor de DAX aplicará su lógica por defecto, lo que puede derivar en visualizaciones incoherentes. Antes de aplicar cualquier fórmula, es fundamental entender los conceptos avanzados de DAX como la transición de contexto, ya que RANKX es un iterador que la ejecuta internamente para cada fila de la tabla proporcionada.
En este artículo analizaremos cómo forzar el comportamiento de RANKX para que los empates dejen de ser un problema técnico y se conviertan en una decisión de diseño controlada.
La anatomía de RANKX y el dilema de los empates
La sintaxis de RANKX es engañosa porque tiene argumentos opcionales que cambian drásticamente el resultado. El quinto argumento, ties, define qué ocurre cuando dos elementos comparten el mismo valor de ranking. Disponemos de dos opciones: Skip (disperso) y Dense (denso).
En un ranking de tipo Skip, si dos elementos ocupan la posición 2, el siguiente elemento saltará a la posición 4. En un ranking Dense, el siguiente elemento ocupará la posición 3. La elección entre uno u otro depende puramente del requerimiento de negocio. Sin embargo, en la mayoría de los cuadros de mando ejecutivos, el ranking denso suele ser el preferido para evitar la sensación de que «faltan datos».
Es importante recordar que RANKX requiere que la tabla que pasamos como primer argumento esté correctamente filtrada. El uso de ALL, ALLSELECTED o VALUES determinará sobre qué universo estamos comparando cada fila. Si estamos trabajando en arquitecturas complejas, como las descritas en cómo crear tablas de dimensión para DWH eficientes, debemos asegurarnos de que la granularidad de la tabla en el ranking coincida con la de la dimensión involucrada.
Estrategias para el desempate determinista
Para evitar que dos elementos compartan la misma posición, debemos introducir un «ruido positivo» o un criterio secundario que diferencie los valores idénticos. En DAX, esto se consigue modificando la expresión que se evalúa en el segundo argumento de la función.
Método 1: El sumando infinitesimal
Una técnica habitual consiste en sumar al valor principal (por ejemplo, Ventas) un valor muy pequeño derivado de otra métrica o de un ID único. Esto garantiza que, ante ventas idénticas, el producto con un margen mayor o simplemente con un ID menor gane la posición superior.
Ranking Producto Desempate =
VAR ValorPrincipal = [Importe Total Ventas]
VAR CriterioSecundario = [Margen Beneficio] * 0.000001
RETURN
RANKX(
ALL(DimProducto[NombreProducto]),
ValorPrincipal + CriterioSecundario,
,
DESC,
Dense
)Este enfoque es extremadamente rápido pero tiene un riesgo: si el criterio secundario es lo suficientemente grande, podría llegar a alterar el ranking principal. Debemos asegurar que el multiplicador (en este caso 0.000001) sea tan pequeño que su impacto solo sea visible en caso de igualdad absoluta en el primer término.
Método 2: Ranking jerárquico con múltiples columnas
En ocasiones, el negocio exige que el desempate sea por orden alfabético. En este caso, no podemos simplemente sumar valores. Debemos construir una lógica que evalúe la posición del nombre dentro del conjunto de datos.
Comparativa: Dense vs Skip
La siguiente tabla resume cómo se comportan las posiciones según el tipo de gestión de empates seleccionado en el quinto argumento de la función RANKX.
| Valor de Métrica | Tipo Skip (Por defecto) | Tipo Dense |
|---|---|---|
| 1.000 € | 1 | 1 |
| 850 € | 2 | 2 |
| 850 € | 2 | 2 |
| 700 € | 4 | 3 |
| 600 € | 5 | 4 |
Optimización y rendimiento: RANKX vs TOPN
Uno de los errores más comunes que veo en proyectos de sector público o energía con grandes volúmenes de datos es el uso de RANKX dentro de un FILTER para mostrar los N mejores elementos. Esto es un error de rendimiento crítico. RANKX debe calcular la posición de cada fila antes de aplicar el filtro, lo cual es computacionalmente costoso en tablas de alta cardinalidad.
Para escenarios de filtrado, es mucho más eficiente utilizar TOPN. Sin embargo, si necesitamos mostrar el número de ranking en la visualización, la combinación ganadora es usar un filtro de informe basado en TOPN y una medida de RANKX solo para la etiqueta. Como menciono en el artículo sobre funciones de tabla en DAX y el coste de la materialización, reducir el número de iteraciones es la clave para mantener un informe fluido.
Si trabajas en entornos DirectQuery, el impacto de estas funciones es aún mayor. Te recomiendo revisar patrones DAX que destruyen el rendimiento en DirectQuery para entender cómo el motor traduce estos rankings a SQL y por qué a veces es mejor realizar el ranking en la capa de transformación.
Consideraciones de diseño en modelos en estrella
Al implementar rankings, la estructura del modelo es vital. Si intentas hacer un ranking sobre una tabla de hechos directamente, el rendimiento se degradará exponencialmente. El ranking siempre debe hacerse sobre columnas de dimensiones (normalizadas y limpias).
- Granularidad: Asegúrate de que la tabla que pasas a
RANKXtiene la granularidad exacta que esperas mostrar en el informe. - Contexto de Filtro: Si quieres que el ranking ignore los filtros del usuario, usa
ALL. Si quieres que el ranking se recalcule según la selección (por ejemplo, ranking de ventas solo en la categoría «Electrónica»), usaALLSELECTED. - Valores en Blanco: Por defecto,
RANKXpone los blancos al final. Si tu medida puede devolver negativos o ceros, el tratamiento de blancos puede desvirtuar el resultado.
Ranking Dinámico =
IF(
NOT ISBLANK([Total Ventas]),
RANKX(
ALLSELECTED(DimProducto[Categoria]),
[Total Ventas],
,
DESC,
Skip
)
)El uso de IF(NOT ISBLANK(...)) es una práctica recomendada para evitar que aparezcan posiciones de ranking en filas donde no hay actividad, algo que ocurre frecuentemente al usar ALL o ALLSELECTED.
Errores frecuentes detectados en auditorías
En mi experiencia como consultor, estos son los fallos más recurrentes al implementar rankings:
- No usar una medida en el segundo argumento: Pasar una expresión directamente sin envolverla en
CALCULATE(o sin usar una medida preexistente) impide la transición de contexto necesaria para que el ranking funcione fila a fila. - Dependencias circulares: Ocurre al intentar crear columnas calculadas de ranking que dependen de otras columnas que a su vez dependen del ranking. Es preferible mantener los rankings como medidas siempre que sea posible.
- Confundir RANKX con la nueva función RANK: Microsoft introdujo recientemente las funciones de ventana como
RANKyROW_NUMBER. Aunque son potentes, su sintaxis basada enORDERBYyPARTITIONBYes distinta y no sustituye aRANKXen todos los casos de uso, especialmente en modelos tabulares clásicos sin cálculo de ventanas complejo.
Conclusión
Controlar RANKX no se trata de memorizar su sintaxis, sino de entender cómo gestiona la visibilidad de los datos a través del contexto de filtro. Definir un criterio de desempate no es un «parche», es una necesidad técnica para asegurar que el usuario confíe en los datos. Un ranking que cambia aleatoriamente cada vez que se refrescan los datos debido a empates no resueltos resta profesionalidad a cualquier solución de Business Intelligence.
Preguntas frecuentes
¿Por qué mi RANKX devuelve siempre 1 en todas las filas?
Esto ocurre porque el contexto de filtro dentro de la iteración no se está modificando correctamente. Asegúrate de pasar una medida en el segundo argumento o de envolver tu expresión en un CALCULATE para forzar la transición de contexto. También verifica que el primer argumento use ALL o ALLSELECTED sobre la tabla/columna que estás visualizando.
¿Qué diferencia hay entre RANKX y la función RANK?
RANKX es una función iteradora clásica muy flexible para medidas. RANK es una función de ventana más reciente que permite particionar los datos (por ejemplo, ranking por categoría sin usar filtros externos) de forma más declarativa y eficiente en ciertos escenarios de gran volumen.
¿Cómo puedo hacer un ranking que dependa de dos medidas distintas?
Puedes crear una medida compuesta que asigne pesos. Por ejemplo: ([Ventas] * 1000) + [Cantidad]. Esto asegura que el ranking principal sea por ventas, y en caso de empate, se use la cantidad como criterio secundario de forma automática y determinista.
Checklist de implementación
- ¿He definido si el ranking debe ser Dense o Skip según el requerimiento?
- ¿He envuelto la expresión en un
IF(NOT ISBLANK(...))para limpiar la visualización? - ¿El primer argumento de
RANKXusaALLSELECTEDpara responder a los filtros del usuario? - ¿He verificado el rendimiento con DAX Studio para asegurar que no hay cuellos de botella?
- ¿Existe un criterio de desempate claro (ID o métrica secundaria) para evitar resultados aleatorios?



