CUME_DIST
CUME_DIST calcula la distribución acumulativa de una fila dentro de su partición. Dicho de forma simple, te dice qué proporción de filas tienen un valor menor o igual al de la fila actual. El resultado es un número entre 0 (exclusivo) y 1 (inclusivo), donde 1 indica que la fila tiene el valor más alto (o igual al más alto) del grupo.
La fórmula que MySQL aplica es: número de filas con valor menor o igual al actual dividido entre el número total de filas en la partición. Si un empleado tiene un CUME_DIST de 0.75 ordenando por salario, significa que el 75% de los empleados ganan lo mismo o menos que él. Es una herramienta fundamental en análisis estadístico y de rendimiento.
Sintaxis
CUME_DIST() OVER (
[PARTITION BY columna1, columna2, ...]
ORDER BY columna1 [ASC|DESC], columna2 [ASC|DESC], ...
)CUME_DIST no acepta argumentos. Su resultado depende completamente del ORDER BY dentro de OVER. Sin ORDER BY, todas las filas tendrían el mismo valor (1.0). El PARTITION BY es opcional y reinicia el cálculo para cada grupo.
Comportamiento básico
Para entender cómo funciona la fórmula, observa este ejemplo con 8 empleados ordenados por salario ascendente:
SELECT
nombre,
salario,
CUME_DIST() OVER (ORDER BY salario) AS cume_dist,
ROUND(CUME_DIST() OVER (ORDER BY salario) * 100, 1) AS percentil
FROM empleados
ORDER BY salario;| nombre | salario | cume_dist | percentil |
|---|---|---|---|
| Pedro Sánchez | 43000.00 | 0.125 | 12.5 |
| Elena Torres | 45000.00 | 0.25 | 25.0 |
| David Moreno | 48000.00 | 0.375 | 37.5 |
| Laura Díaz | 52000.00 | 0.5 | 50.0 |
| Marta Ruiz | 55000.00 | 0.625 | 62.5 |
| Ana García | 58000.00 | 0.75 | 75.0 |
| Roberto Fernández | 65000.00 | 0.875 | 87.5 |
| Carlos López | 72000.00 | 1.0 | 100.0 |
Pedro, con el salario más bajo, tiene un CUME_DIST de 0.125 (1/8): solo el 12.5% de los empleados gana lo mismo o menos que él (él mismo). Laura, en el punto medio, tiene 0.5: el 50% gana lo mismo o menos. Carlos, con el salario más alto, tiene 1.0: todos (el 100%) ganan lo mismo o menos que él.
Comportamiento con empates
Cuando hay valores iguales, CUME_DIST les asigna el mismo valor, porque el mismo porcentaje de filas tiene un valor menor o igual:
SELECT
nombre,
salario,
CUME_DIST() OVER (ORDER BY salario) AS cume_dist
FROM empleados
ORDER BY salario;| nombre | salario | cume_dist |
|---|---|---|
| Pedro Sánchez | 43000.00 | 0.125 |
| David Moreno | 48000.00 | 0.375 |
| Elena Torres | 48000.00 | 0.375 |
| Laura Díaz | 52000.00 | 0.5 |
| Ana García | 55000.00 | 0.75 |
| Marta Ruiz | 55000.00 | 0.75 |
| Roberto Fernández | 65000.00 | 0.875 |
| Carlos López | 72000.00 | 1.0 |
David y Elena, ambos con 48000, comparten el CUME_DIST de 0.375 porque 3 de 8 empleados (37.5%) ganan 48000 o menos. Ana y Marta comparten 0.75 porque 6 de 8 (75%) ganan 55000 o menos. Nota que se salta del 0.125 al 0.375 (no hay 0.25) porque los empates agrupan las posiciones.
Caso práctico: identificar el top 10% de clientes
Un uso clásico de CUME_DIST es identificar los clientes más valiosos. El top 10% son aquellos cuyo gasto acumulado supera al 90% del resto:
WITH clientes_ranking AS (
SELECT
c.nombre,
SUM(p.total) AS gasto_total,
CUME_DIST() OVER (ORDER BY SUM(p.total)) AS cume_dist
FROM clientes c
JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.id_cliente, c.nombre
)
SELECT
nombre,
gasto_total,
ROUND(cume_dist * 100, 1) AS percentil
FROM clientes_ranking
WHERE cume_dist > 0.90
ORDER BY gasto_total DESC;| nombre | gasto_total | percentil |
|---|---|---|
| Empresa Textil Norte S.L. | 25400.00 | 100.0 |
| Distribuciones García | 22800.00 | 99.6 |
| Restaurante El Molino | 19500.00 | 99.2 |
| Ferretería López e Hijos | 18200.00 | 98.8 |
| Consultora Digital ABC | 16900.00 | 98.4 |
El filtro cume_dist > 0.90 selecciona solo a los clientes que están por encima del percentil 90, es decir, el 10% con mayor gasto. Estos son los clientes VIP que merecen atención personalizada.
Caso práctico: segmentación por percentiles
Puedes usar CUME_DIST para crear segmentos basados en percentiles, que son más precisos que los cuartiles de NTILE porque respetan los empates:
SELECT
nombre,
departamento,
salario,
ROUND(CUME_DIST() OVER (ORDER BY salario) * 100, 1) AS percentil,
CASE
WHEN CUME_DIST() OVER (ORDER BY salario) <= 0.25 THEN 'Q1 - Bajo'
WHEN CUME_DIST() OVER (ORDER BY salario) <= 0.50 THEN 'Q2 - Medio-bajo'
WHEN CUME_DIST() OVER (ORDER BY salario) <= 0.75 THEN 'Q3 - Medio-alto'
ELSE 'Q4 - Alto'
END AS segmento
FROM empleados
ORDER BY salario;| nombre | departamento | salario | percentil | segmento |
|---|---|---|---|---|
| Pedro Sánchez | Ventas | 43000.00 | 12.5 | Q1 - Bajo |
| Elena Torres | Soporte | 45000.00 | 25.0 | Q1 - Bajo |
| David Moreno | Soporte | 48000.00 | 37.5 | Q2 - Medio-bajo |
| Laura Díaz | Marketing | 52000.00 | 50.0 | Q2 - Medio-bajo |
| Marta Ruiz | Ventas | 55000.00 | 62.5 | Q3 - Medio-alto |
| Ana García | Ventas | 58000.00 | 75.0 | Q3 - Medio-alto |
| Roberto Fernández | Marketing | 65000.00 | 87.5 | Q4 - Alto |
| Carlos López | Ventas | 72000.00 | 100.0 | Q4 - Alto |
La diferencia con NTILE(4) es sutil pero importante: NTILE distribuye las filas en grupos de tamaño igual sin considerar los valores, mientras que CUME_DIST calcula la posición relativa real basada en los valores. Con empates, CUME_DIST puede producir segmentos de distinto tamaño.
Caso práctico: análisis de rendimiento por departamento
Para comparar cómo se distribuyen los rendimientos dentro de cada departamento, particiona por departamento:
SELECT
nombre,
departamento,
total_ventas,
ROUND(CUME_DIST() OVER (
PARTITION BY departamento
ORDER BY total_ventas
) * 100, 1) AS percentil_depto,
ROUND(CUME_DIST() OVER (
ORDER BY total_ventas
) * 100, 1) AS percentil_global
FROM empleados
ORDER BY departamento, total_ventas;| nombre | departamento | total_ventas | percentil_depto | percentil_global |
|---|---|---|---|---|
| Laura Díaz | Marketing | 115000.00 | 50.0 | 25.0 |
| Roberto Fernández | Marketing | 142000.00 | 100.0 | 62.5 |
| Pedro Sánchez | Ventas | 87000.00 | 25.0 | 12.5 |
| Marta Ruiz | Ventas | 128000.00 | 50.0 | 37.5 |
| Ana García | Ventas | 142000.00 | 75.0 | 62.5 |
| Carlos López | Ventas | 185000.00 | 100.0 | 100.0 |
Laura está en el percentil 50 de Marketing (es la peor de 2) pero en el percentil 25 global. Roberto está en el percentil 100 de Marketing (el mejor de su departamento) pero solo en el 62.5 global. Esta doble perspectiva revela que ser el mejor de un departamento pequeño no implica estar entre los mejores a nivel empresa.
Manejo de NULL
Los valores NULL en la columna de orden se tratan como equivalentes entre sí y se ordenan primero en orden ascendente (el comportamiento por defecto de MySQL). Todos los NULL reciben el mismo CUME_DIST:
SELECT
nombre,
comision,
ROUND(CUME_DIST() OVER (ORDER BY comision) * 100, 1) AS percentil
FROM empleados;| nombre | comision | percentil |
|---|---|---|
| Pedro Sánchez | NULL | 37.5 |
| Elena Torres | NULL | 37.5 |
| David Moreno | NULL | 37.5 |
| Roberto Fernández | 3000.00 | 50.0 |
| Marta Ruiz | 5000.00 | 75.0 |
| Ana García | 5000.00 | 75.0 |
| Carlos López | 8000.00 | 87.5 |
| Laura Díaz | 12000.00 | 100.0 |
Los tres empleados sin comisión comparten el percentil 37.5 (3 de 8 = 0.375). Si prefieres excluirlos del análisis, filtra con WHERE comision IS NOT NULL.
Combinación con otras funciones
CUME_DIST se combina con funciones de formato y condicionales para crear informes de distribución claros:
SELECT
nombre,
salario,
ROUND(CUME_DIST() OVER (ORDER BY salario) * 100, 0) AS percentil,
CONCAT(
'Percentil ',
ROUND(CUME_DIST() OVER (ORDER BY salario) * 100, 0)
) AS etiqueta,
ROUND(salario - AVG(salario) OVER (), 2) AS vs_promedio,
CASE
WHEN CUME_DIST() OVER (ORDER BY salario) >= 0.9 THEN 'Top 10%'
WHEN CUME_DIST() OVER (ORDER BY salario) >= 0.75 THEN 'Top 25%'
WHEN CUME_DIST() OVER (ORDER BY salario) >= 0.5 THEN 'Mitad superior'
ELSE 'Mitad inferior'
END AS clasificacion
FROM empleados
ORDER BY salario DESC;| nombre | salario | percentil | etiqueta | vs_promedio | clasificacion |
|---|---|---|---|---|---|
| Carlos López | 72000.00 | 100 | Percentil 100 | 17250.00 | Top 10% |
| Roberto Fernández | 65000.00 | 88 | Percentil 88 | 10250.00 | Top 25% |
| Ana García | 58000.00 | 75 | Percentil 75 | 3250.00 | Top 25% |
| Marta Ruiz | 55000.00 | 63 | Percentil 63 | 250.00 | Mitad superior |
| Laura Díaz | 52000.00 | 50 | Percentil 50 | -2750.00 | Mitad superior |
| David Moreno | 48000.00 | 38 | Percentil 38 | -6750.00 | Mitad inferior |
El informe combina el percentil exacto, una etiqueta legible, la desviación respecto al promedio y una clasificación por tramos. Cada empleado puede ver exactamente dónde se sitúa en la distribución salarial de la empresa.
En el siguiente artículo veremos PERCENT_RANK para calcular el rango porcentual.
Escrito por Eduardo Lázaro
