sqlpostgresqlaggregationanalytics

PERCENTILE_DISC en SQL: percentiles discretos sin interpolacion

Como PERCENTILE_DISC devuelve un valor real de tus datos sin interpolacion, en que se diferencia de PERCENTILE_CONT y cuando elegir la variante discreta.

2 min de lecturaReferencesql · postgresql · aggregation · analytics · statistics

PERCENTILE_DISC devuelve un percentil como uno de los valores que realmente existen en tus datos, sin interpolar entre vecinos. Eso lo convierte en la opcion correcta cuando "el promedio de dos filas" no tiene sentido: para categorias, precios discretos y cualquier caso en que el resultado deba ser un valor real de la tabla.

Sintaxis basica

PERCENTILE_DISC es una agregacion de conjunto ordenado (ordered-set aggregate), por eso usa la sintaxis especial WITHIN GROUP (ORDER BY ...). El argumento entre parentesis es una fraccion entre 0 y 1.

-- Median order amount as a real value from the data
SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY amount) AS median_amount
FROM orders;

-- 90th percentile of salaries
SELECT PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY salary) AS p90_salary
FROM employees;

Como se elige el valor: las filas se ordenan por la clausula ORDER BY y luego se devuelve el primer valor cuya fraccion acumulada de filas alcanza el objetivo. El resultado siempre es igual a uno de los valores de la columna.

Discreto frente a continuo

PERCENTILE_CONT interpola: con un numero par de filas la mediana es el promedio de las dos centrales. PERCENTILE_DISC no interpola; entrega un valor real. Compara ambos en una sola consulta:

-- For amounts 100, 200, 300, 400:
-- CONT median = 250 (interpolated), DISC median = 200 (real value)
SELECT
  PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY amount) AS disc_median,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS cont_median
FROM orders;

Diferencias clave:

  • DISC siempre devuelve un valor presente en los datos; CONT puede devolver un numero que no esta en la tabla.
  • CONT requiere un tipo numerico (o intervalo). DISC tambien funciona con texto, fechas y enums, cualquier tipo ordenable.
  • En muestras grandes sus resultados convergen, pero en pequenas divergen de forma notable.

Cuando elegir el discreto

Usa PERCENTILE_DISC cuando un valor intermedio carece de sentido o es invalido:

  • Datos categoricos y de texto: un estado o pais "mediano" debe ser una cadena real, no una invencion.
  • Importes y precios discretos donde no puede existir "250.5".
  • SLA e informes donde debes mostrar un valor concreto y observado.
-- Median country code per status: must be a real value, not interpolated
SELECT o.status,
       PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY u.country) AS median_country
FROM orders o
JOIN users u ON u.id = o.user_id
GROUP BY o.status;

Aqui PERCENTILE_CONT simplemente no se ejecutara: no puedes interpolar cadenas de country.

Varios percentiles en una sola pasada

En PostgreSQL puedes pasar un array de fracciones y obtener un array de resultados con una sola llamada, lo que es mas barato que tres consultas separadas porque el ordenamiento ocurre una vez.

-- p50, p90, p99 in a single ordered pass
SELECT PERCENTILE_DISC(ARRAY[0.5, 0.9, 0.99])
         WITHIN GROUP (ORDER BY amount) AS p
FROM orders;
-- p -> {200, 980, 1500}

Es comodo desplegar el array en columnas de inmediato:

SELECT p[1] AS p50, p[2] AS p90, p[3] AS p99
FROM (
  SELECT PERCENTILE_DISC(ARRAY[0.5, 0.9, 0.99])
           WITHIN GROUP (ORDER BY amount) AS p
  FROM orders
) t;

Combina muy bien con GROUP BY cuando necesitas percentiles por segmento:

SELECT dept,
       PERCENTILE_DISC(ARRAY[0.25, 0.5, 0.75])
         WITHIN GROUP (ORDER BY salary) AS quartiles
FROM employees
GROUP BY dept;

Trampas y diferencias entre motores

  • NULL en el ORDER BY se ignora al calcular la posicion, igual que en otros agregados. Suele ser lo que quieres, pero verificalo si los NULL son significativos.
  • No puedes reemplazar WITHIN GROUP por un ORDER BY normal dentro de los parentesis; eso es un error de sintaxis.
  • MySQL no tiene PERCENTILE_DISC en absoluto; los percentiles se emulan con funciones de ventana y NTILE/ROW_NUMBER.
  • En ClickHouse el equivalente es quantileExact(0.5)(amount): tambien devuelve un valor real de los datos, mientras que quantile() interpola.
-- ClickHouse: exact (discrete) vs interpolated
SELECT
  quantileExact(0.5)(amount) AS disc_median,
  quantile(0.5)(amount)      AS cont_median
FROM orders;

La regla decisiva: si el resultado debe leerse como "una fila real de la tabla", usa PERCENTILE_DISC; si quieres una estimacion estadistica suave del centro o de una cola, usa PERCENTILE_CONT.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador