RANK
RANK asigna un número de rango a cada fila dentro de su partición según el orden especificado. Cuando dos o más filas tienen el mismo valor en la columna de orden, reciben el mismo rango, y el siguiente rango se incrementa teniendo en cuenta cuántas filas empataron. Esto produce huecos en la secuencia: si dos filas comparten el rango 2, el siguiente rango será 4, no 3.
Este comportamiento es exactamente el que se usa en competiciones deportivas. Si dos atletas terminan en segundo lugar, no hay tercer lugar: el siguiente puesto es el cuarto. Por eso RANK es la función natural cuando modelas clasificaciones donde los empates deben reflejarse con fidelidad.
Sintaxis
RANK() OVER (
[PARTITION BY columna1, columna2, ...]
ORDER BY columna1 [ASC|DESC], columna2 [ASC|DESC], ...
)Al igual que ROW_NUMBER, RANK no acepta argumentos. Su comportamiento se define completamente por la cláusula OVER. El ORDER BY es obligatorio en la práctica: sin él, todas las filas recibirían el rango 1 ya que no habría criterio para diferenciarlas.
Comportamiento básico
Observa cómo RANK asigna rangos cuando hay valores repetidos en la columna de orden:
SELECT
RANK() OVER (ORDER BY total_ventas DESC) AS ranking,
nombre,
departamento,
total_ventas
FROM empleados;| ranking | nombre | departamento | total_ventas |
|---|---|---|---|
| 1 | Carlos López | Ventas | 185000.00 |
| 2 | Roberto Fernández | Marketing | 142000.00 |
| 2 | Ana García | Ventas | 142000.00 |
| 4 | Marta Ruiz | Ventas | 128000.00 |
| 5 | Laura Díaz | Marketing | 115000.00 |
| 6 | David Moreno | Soporte | 98000.00 |
| 6 | Elena Torres | Soporte | 98000.00 |
| 8 | Pedro Sánchez | Ventas | 87000.00 |
Roberto y Ana comparten el rango 2 porque tienen el mismo total de ventas. El siguiente rango es 4, no 3, porque hay dos personas en el puesto 2. Lo mismo ocurre con David y Elena en el rango 6: el siguiente es 8. Los huecos en la numeración (no hay 3, no hay 7) son la característica definitoria de RANK.
RANK con PARTITION BY
Al particionar, el ranking se reinicia en cada grupo:
SELECT
RANK() OVER (
PARTITION BY departamento
ORDER BY total_ventas DESC
) AS ranking_depto,
nombre,
departamento,
total_ventas
FROM empleados;| ranking_depto | nombre | departamento | total_ventas |
|---|---|---|---|
| 1 | Roberto Fernández | Marketing | 142000.00 |
| 2 | Laura Díaz | Marketing | 115000.00 |
| 1 | Carlos López | Ventas | 185000.00 |
| 2 | Ana García | Ventas | 142000.00 |
| 3 | Marta Ruiz | Ventas | 128000.00 |
| 4 | Pedro Sánchez | Ventas | 87000.00 |
| 1 | David Moreno | Soporte | 98000.00 |
| 2 | Elena Torres | Soporte | 95000.00 |
Cada departamento tiene su propio ranking independiente. Dentro de Marketing, Roberto es primero y Laura segunda. Dentro de Ventas, la secuencia va del 1 al 4.
Caso práctico: ranking de ventas mensuales
Supón que quieres crear un ranking mensual de vendedores para un programa de incentivos. Los empates importan porque si dos vendedores alcanzan la misma cifra, ambos merecen la misma bonificación:
SELECT
RANK() OVER (
PARTITION BY mes
ORDER BY monto_ventas DESC
) AS posicion,
nombre,
mes,
monto_ventas,
CASE
WHEN RANK() OVER (PARTITION BY mes ORDER BY monto_ventas DESC) = 1
THEN 'Oro'
WHEN RANK() OVER (PARTITION BY mes ORDER BY monto_ventas DESC) <= 3
THEN 'Plata'
ELSE 'Participante'
END AS categoria_incentivo
FROM ventas_empleados
WHERE mes IN ('2024-10', '2024-11');| posicion | nombre | mes | monto_ventas | categoria_incentivo |
|---|---|---|---|---|
| 1 | Carlos López | 2024-10 | 32000.00 | Oro |
| 2 | Ana García | 2024-10 | 28500.00 | Plata |
| 2 | Marta Ruiz | 2024-10 | 28500.00 | Plata |
| 4 | Pedro Sánchez | 2024-10 | 21000.00 | Participante |
| 1 | Ana García | 2024-11 | 35200.00 | Oro |
| 2 | Carlos López | 2024-11 | 33800.00 | Plata |
| 3 | Marta Ruiz | 2024-11 | 29100.00 | Plata |
| 4 | Pedro Sánchez | 2024-11 | 22400.00 | Participante |
En octubre, Ana y Marta empatan en segundo lugar y ambas reciben la categoría Plata. Pedro queda en posición 4 (no 3) debido al empate, pero eso no afecta su categoría porque el filtro es <= 3.
Caso práctico: calificaciones de estudiantes
RANK es natural para clasificar notas donde los empates son frecuentes. Imagina un sistema de calificaciones por asignatura:
SELECT
a.nombre AS asignatura,
e.nombre AS estudiante,
c.nota_final,
RANK() OVER (
PARTITION BY a.id_asignatura
ORDER BY c.nota_final DESC
) AS posicion,
CASE
WHEN RANK() OVER (PARTITION BY a.id_asignatura ORDER BY c.nota_final DESC) = 1
THEN 'Matrícula de Honor'
ELSE ''
END AS distincion
FROM calificaciones c
JOIN estudiantes e ON c.id_estudiante = e.id_estudiante
JOIN asignaturas a ON c.id_asignatura = a.id_asignatura
WHERE a.nombre = 'Bases de Datos'
ORDER BY posicion;| asignatura | estudiante | nota_final | posicion | distincion |
|---|---|---|---|---|
| Bases de Datos | Lucía Martín | 9.80 | 1 | Matrícula de Honor |
| Bases de Datos | Diego Navarro | 9.50 | 2 | |
| Bases de Datos | Sara Romero | 9.50 | 2 | |
| Bases de Datos | Pablo Gil | 8.75 | 4 | |
| Bases de Datos | Irene Molina | 8.20 | 5 | |
| Bases de Datos | Adrián Serrano | 7.90 | 6 |
Diego y Sara empatan con un 9.50 y comparten la segunda posición. Pablo, con 8.75, queda en cuarta posición. Si esta clasificación determinase quiénes reciben beca (los tres primeros), tanto Diego como Sara entrarían, y el siguiente candidato sería el cuarto.
Caso práctico: ranking global vs departamental
Un análisis interesante es mostrar a cada empleado tanto su posición global como su posición dentro de su departamento:
SELECT
nombre,
departamento,
total_ventas,
RANK() OVER (ORDER BY total_ventas DESC) AS ranking_global,
RANK() OVER (
PARTITION BY departamento
ORDER BY total_ventas DESC
) AS ranking_depto
FROM empleados
ORDER BY ranking_global;| nombre | departamento | total_ventas | ranking_global | ranking_depto |
|---|---|---|---|---|
| Carlos López | Ventas | 185000.00 | 1 | 1 |
| Roberto Fernández | Marketing | 142000.00 | 2 | 1 |
| Ana García | Ventas | 142000.00 | 2 | 2 |
| Marta Ruiz | Ventas | 128000.00 | 4 | 3 |
| Laura Díaz | Marketing | 115000.00 | 5 | 2 |
| David Moreno | Soporte | 98000.00 | 6 | 1 |
Roberto es el segundo a nivel global pero el primero en Marketing. David es sexto globalmente pero lidera Soporte. Esta doble perspectiva es valiosa en evaluaciones de rendimiento donde importa tanto la posición absoluta como la relativa al equipo.
Manejo de NULL
Los valores NULL en la columna de orden reciben el mismo rango entre sí, ya que MySQL los considera equivalentes a efectos de comparación:
SELECT
RANK() OVER (ORDER BY comision DESC) AS ranking,
nombre,
comision
FROM empleados;| ranking | nombre | comision |
|---|---|---|
| 1 | Carlos López | 15000.00 |
| 2 | Ana García | 12000.00 |
| 3 | Marta Ruiz | 8000.00 |
| 4 | Pedro Sánchez | NULL |
| 4 | Elena Torres | NULL |
| 4 | David Moreno | NULL |
Los tres empleados sin comisión comparten el rango 4 porque NULL se considera igual a NULL en el contexto del ranking. Si necesitas que los NULL no participen en el ranking, filtra con WHERE comision IS NOT NULL.
Combinación con otras funciones
RANK se combina con funciones de agregación de ventana para crear informes de clasificación enriquecidos con contexto estadístico:
SELECT
RANK() OVER (ORDER BY total_ventas DESC) AS posicion,
nombre,
total_ventas,
ROUND(total_ventas - AVG(total_ventas) OVER (), 2) AS vs_promedio,
ROUND(total_ventas / MAX(total_ventas) OVER () * 100, 1) AS pct_del_lider
FROM empleados
ORDER BY posicion;| posicion | nombre | total_ventas | vs_promedio | pct_del_lider |
|---|---|---|---|---|
| 1 | Carlos López | 185000.00 | 63625.00 | 100.0 |
| 2 | Roberto Fernández | 142000.00 | 20625.00 | 76.8 |
| 2 | Ana García | 142000.00 | 20625.00 | 76.8 |
| 4 | Marta Ruiz | 128000.00 | 6625.00 | 69.2 |
| 5 | Laura Díaz | 115000.00 | -6375.00 | 62.2 |
Cada fila muestra no solo la posición del empleado, sino cuánto está por encima o por debajo del promedio y qué porcentaje alcanza respecto al líder. Esto convierte un ranking simple en un cuadro de mando completo.
Practica con RANK
Usa el editor para asignar rankings con posibles saltos:
En el siguiente artículo veremos DENSE_RANK para rangos sin huecos.
Escrito por Eduardo Lázaro
