PERCENT_RANK
PERCENT_RANK calcula el rango porcentual de cada fila dentro de su partición, devolviendo un valor entre 0 y 1. La primera fila siempre recibe 0 y la última recibe 1, distribuyendo las demás proporcionalmente entre ambos extremos. Es similar a CUME_DIST pero usa una fórmula diferente que resulta más intuitiva para ciertos análisis, especialmente cuando necesitas saber qué porcentaje de valores está estrictamente por debajo de la fila actual.
La fórmula que MySQL aplica es: (rango de la fila - 1) dividido entre (número total de filas en la partición - 1). Si hay solo una fila, el resultado es 0. Esta fórmula garantiza que el primer valor sea siempre 0 y el último siempre 1, creando una escala uniforme.
Sintaxis
PERCENT_RANK() OVER (
[PARTITION BY columna1, columna2, ...]
ORDER BY columna1 [ASC|DESC], columna2 [ASC|DESC], ...
)Al igual que CUME_DIST, PERCENT_RANK no acepta argumentos y su resultado depende del ORDER BY dentro de OVER. Sin ORDER BY, todas las filas obtienen 0.
Comportamiento básico
Para ver cómo la fórmula produce los valores, observa este ejemplo con 8 empleados:
SELECT
nombre,
salario,
RANK() OVER (ORDER BY salario) AS rango,
ROUND(PERCENT_RANK() OVER (ORDER BY salario), 4) AS pct_rank,
ROUND(PERCENT_RANK() OVER (ORDER BY salario) * 100, 1) AS percentil
FROM empleados
ORDER BY salario;| nombre | salario | rango | pct_rank | percentil |
|---|---|---|---|---|
| Pedro Sánchez | 43000.00 | 1 | 0.0000 | 0.0 |
| Elena Torres | 45000.00 | 2 | 0.1429 | 14.3 |
| David Moreno | 48000.00 | 3 | 0.2857 | 28.6 |
| Laura Díaz | 52000.00 | 4 | 0.4286 | 42.9 |
| Marta Ruiz | 55000.00 | 5 | 0.5714 | 57.1 |
| Ana García | 58000.00 | 6 | 0.7143 | 71.4 |
| Roberto Fernández | 65000.00 | 7 | 0.8571 | 85.7 |
| Carlos López | 72000.00 | 8 | 1.0000 | 100.0 |
La fórmula (rango - 1) / (total - 1) produce: Pedro = (1-1)/(8-1) = 0, Elena = (2-1)/7 = 0.1429, y así sucesivamente hasta Carlos = (8-1)/7 = 1.0. El resultado se distribuye uniformemente entre 0 y 1.
Comportamiento con empates
Cuando hay empates, las filas empatadas comparten el mismo rango y por tanto el mismo PERCENT_RANK:
SELECT
nombre,
salario,
RANK() OVER (ORDER BY salario) AS rango,
ROUND(PERCENT_RANK() OVER (ORDER BY salario), 4) AS pct_rank
FROM empleados
ORDER BY salario;| nombre | salario | rango | pct_rank |
|---|---|---|---|
| Pedro Sánchez | 43000.00 | 1 | 0.0000 |
| David Moreno | 48000.00 | 2 | 0.1429 |
| Elena Torres | 48000.00 | 2 | 0.1429 |
| Laura Díaz | 52000.00 | 4 | 0.4286 |
| Ana García | 55000.00 | 5 | 0.5714 |
| Marta Ruiz | 55000.00 | 5 | 0.5714 |
| Roberto Fernández | 65000.00 | 7 | 0.8571 |
| Carlos López | 72000.00 | 8 | 1.0000 |
David y Elena comparten el rango 2, así que ambos tienen PERCENT_RANK = (2-1)/7 = 0.1429. El siguiente rango es 4 (salto por el empate), que produce (4-1)/7 = 0.4286. Los empates crean saltos en la distribución pero el primer y último valor siempre son 0 y 1 respectivamente.
Diferencia con CUME_DIST
La diferencia entre PERCENT_RANK y CUME_DIST es sutil pero importante. Comparémoslas lado a lado:
SELECT
nombre,
salario,
ROUND(PERCENT_RANK() OVER (ORDER BY salario) * 100, 1) AS pct_rank,
ROUND(CUME_DIST() OVER (ORDER BY salario) * 100, 1) AS cume_dist
FROM empleados
ORDER BY salario;| nombre | salario | pct_rank | cume_dist |
|---|---|---|---|
| Pedro Sánchez | 43000.00 | 0.0 | 12.5 |
| Elena Torres | 45000.00 | 14.3 | 25.0 |
| David Moreno | 48000.00 | 28.6 | 37.5 |
| Laura Díaz | 52000.00 | 42.9 | 50.0 |
| Marta Ruiz | 55000.00 | 57.1 | 62.5 |
| Ana García | 58000.00 | 71.4 | 75.0 |
| Roberto Fernández | 65000.00 | 85.7 | 87.5 |
| Carlos López | 72000.00 | 100.0 | 100.0 |
Las diferencias clave son tres. Primera, PERCENT_RANK empieza en 0 mientras que CUME_DIST nunca es 0 (su valor mínimo es 1/N). Segunda, la fórmula de PERCENT_RANK es (rango-1)/(total-1), mientras que CUME_DIST usa filas_menores_o_iguales/total. Tercera, PERCENT_RANK responde "qué porcentaje de valores están estrictamente por debajo", mientras que CUME_DIST responde "qué porcentaje de valores están por debajo o son iguales".
La elección entre una y otra depende de la pregunta. Si quieres saber "el 75% de los empleados ganan menos o igual que Ana", usa CUME_DIST. Si quieres saber "Ana supera al 71.4% de los empleados", usa PERCENT_RANK.
Caso práctico: distribución salarial de la empresa
PERCENT_RANK es ideal para visualizar cómo se distribuyen los salarios en toda la organización y detectar desigualdades:
SELECT
nombre,
departamento,
salario,
ROUND(PERCENT_RANK() OVER (ORDER BY salario) * 100, 1) AS percentil_empresa,
ROUND(PERCENT_RANK() OVER (
PARTITION BY departamento
ORDER BY salario
) * 100, 1) AS percentil_depto
FROM empleados
ORDER BY salario;| nombre | departamento | salario | percentil_empresa | percentil_depto |
|---|---|---|---|---|
| Pedro Sánchez | Ventas | 43000.00 | 0.0 | 0.0 |
| Elena Torres | Soporte | 45000.00 | 14.3 | 0.0 |
| David Moreno | Soporte | 48000.00 | 28.6 | 100.0 |
| Laura Díaz | Marketing | 52000.00 | 42.9 | 0.0 |
| Marta Ruiz | Ventas | 55000.00 | 57.1 | 33.3 |
| Ana García | Ventas | 58000.00 | 71.4 | 66.7 |
| Roberto Fernández | Marketing | 65000.00 | 85.7 | 100.0 |
| Carlos López | Ventas | 72000.00 | 100.0 | 100.0 |
Elena está en el percentil 0 dentro de Soporte (es la peor pagada de su departamento) pero en el 14.3 de la empresa. David, con solo 3000 más de salario, está en el percentil 100 de Soporte (el mejor de su departamento de 2 personas) pero solo en el 28.6 global. Esta comparación revela que el "mejor pagado" de Soporte gana menos que la mediana de la empresa.
Caso práctico: clasificación por percentiles
Los percentiles permiten crear clasificaciones estándar como las que se usan en evaluaciones educativas:
SELECT
nombre,
nota_final,
ROUND(PERCENT_RANK() OVER (ORDER BY nota_final) * 100, 0) AS percentil,
CASE
WHEN PERCENT_RANK() OVER (ORDER BY nota_final) >= 0.90 THEN 'Sobresaliente'
WHEN PERCENT_RANK() OVER (ORDER BY nota_final) >= 0.70 THEN 'Notable'
WHEN PERCENT_RANK() OVER (ORDER BY nota_final) >= 0.50 THEN 'Bien'
WHEN PERCENT_RANK() OVER (ORDER BY nota_final) >= 0.30 THEN 'Suficiente'
ELSE 'Insuficiente'
END AS calificacion
FROM calificaciones
WHERE id_asignatura = 101
ORDER BY nota_final DESC;| nombre | nota_final | percentil | calificacion |
|---|---|---|---|
| Lucía Martín | 9.80 | 100 | Sobresaliente |
| Diego Navarro | 9.50 | 90 | Sobresaliente |
| Sara Romero | 8.75 | 80 | Notable |
| Pablo Gil | 8.20 | 70 | Notable |
| Irene Molina | 7.50 | 60 | Bien |
| Adrián Serrano | 7.10 | 50 | Bien |
| Marcos Vidal | 6.80 | 40 | Suficiente |
| Clara Jiménez | 6.20 | 30 | Suficiente |
| Hugo Prieto | 5.50 | 20 | Insuficiente |
| Nerea Castro | 4.80 | 10 | Insuficiente |
| Álvaro Peña | 3.90 | 0 | Insuficiente |
Los percentiles asignan automáticamente las calificaciones según la distribución real de las notas, no según umbrales fijos. Si todos los estudiantes obtienen notas altas, el percentil 30 podría ser un 7.5, mientras que en un grupo con notas bajas podría ser un 4.0.
Caso práctico: análisis de precios en el mercado
Para entender la posición competitiva de tus productos, PERCENT_RANK muestra dónde se sitúa cada precio dentro de la categoría:
SELECT
nombre,
categoria,
precio,
ROUND(PERCENT_RANK() OVER (
PARTITION BY categoria
ORDER BY precio
) * 100, 1) AS percentil_precio,
CASE
WHEN PERCENT_RANK() OVER (PARTITION BY categoria ORDER BY precio) >= 0.75
THEN 'Premium'
WHEN PERCENT_RANK() OVER (PARTITION BY categoria ORDER BY precio) >= 0.25
THEN 'Rango medio'
ELSE 'Económico'
END AS posicionamiento
FROM productos
WHERE activo = 1
ORDER BY categoria, precio;| nombre | categoria | precio | percentil_precio | posicionamiento |
|---|---|---|---|---|
| Ratón inalámbrico | Electrónica | 29.90 | 0.0 | Económico |
| Teclado mecánico RGB | Electrónica | 129.00 | 33.3 | Rango medio |
| Auriculares Pro | Electrónica | 189.00 | 66.7 | Rango medio |
| Monitor 27" 4K | Electrónica | 349.00 | 100.0 | Premium |
| Camiseta algodón | Ropa | 25.00 | 0.0 | Económico |
| Pantalón chino | Ropa | 49.90 | 50.0 | Rango medio |
| Abrigo lana italiano | Ropa | 245.00 | 100.0 | Premium |
El teclado mecánico está en el percentil 33 de Electrónica, lo que indica que un tercio de los productos de la categoría son más baratos. El monitor, en el percentil 100, es el más caro de su categoría.
Manejo de NULL
PERCENT_RANK trata los valores NULL de la misma forma que RANK: se agrupan juntos y reciben el mismo rango. En orden ascendente, los NULL aparecen primero y reciben el rango 1, que produce PERCENT_RANK = 0:
SELECT
nombre,
comision,
ROUND(PERCENT_RANK() OVER (ORDER BY comision) * 100, 1) AS pct_rank
FROM empleados;| nombre | comision | pct_rank |
|---|---|---|
| Pedro Sánchez | NULL | 0.0 |
| Elena Torres | NULL | 0.0 |
| David Moreno | NULL | 0.0 |
| Roberto Fernández | 3000.00 | 42.9 |
| Marta Ruiz | 5000.00 | 57.1 |
Los tres empleados sin comisión comparten el rango 1 y por tanto PERCENT_RANK = 0. El primer empleado con comisión (Roberto) salta al 42.9% porque 3 de los 7 empleados están por debajo de él. Si necesitas excluir los NULL del cálculo, filtra antes con WHERE comision IS NOT NULL.
Combinación con otras funciones
PERCENT_RANK se combina con funciones de agregación y formato para crear informes de distribución detallados:
SELECT
nombre,
departamento,
salario,
ROUND(PERCENT_RANK() OVER w * 100, 1) AS percentil,
ROUND(AVG(salario) OVER (), 2) AS media_empresa,
salario - ROUND(AVG(salario) OVER (), 2) AS vs_media,
CONCAT(
ROUND(PERCENT_RANK() OVER w * 100, 0),
'% de empleados ganan menos'
) AS interpretacion
FROM empleados
WINDOW w AS (ORDER BY salario)
ORDER BY salario DESC;| nombre | departamento | salario | percentil | media_empresa | vs_media | interpretacion |
|---|---|---|---|---|---|---|
| Carlos López | Ventas | 72000.00 | 100.0 | 54750.00 | 17250.00 | 100% de empleados ganan menos |
| Roberto Fernández | Marketing | 65000.00 | 85.7 | 54750.00 | 10250.00 | 86% de empleados ganan menos |
| Ana García | Ventas | 58000.00 | 71.4 | 54750.00 | 3250.00 | 71% de empleados ganan menos |
| Marta Ruiz | Ventas | 55000.00 | 57.1 | 54750.00 | 250.00 | 57% de empleados ganan menos |
| Laura Díaz | Marketing | 52000.00 | 42.9 | 54750.00 | -2750.00 | 43% de empleados ganan menos |
| David Moreno | Soporte | 48000.00 | 28.6 | 54750.00 | -6750.00 | 29% de empleados ganan menos |
La columna de interpretación traduce el número abstracto del percentil en una frase que cualquier persona puede entender: "el 86% de los empleados ganan menos que Roberto". Combinando esto con la desviación respecto a la media, el informe ofrece una visión completa de la posición relativa de cada empleado.
En la siguiente sección veremos las funciones de control de flujo como CASE, IF y COALESCE.
Escrito por Eduardo Lázaro
