WIDTH_BUCKET responde a una pregunta sencilla: en que tramo del histograma cae un numero si dividimos un rango en partes de igual anchura. Es la herramienta ideal cuando necesitas agrupar un valor continuo (importe de un pedido, salario, puntuacion) en cubos uniformes y construir una distribucion.
Firma y logica
La llamada canonica en PostgreSQL recibe cuatro argumentos:
SELECT WIDTH_BUCKET(score, 0, 100, 10) AS bucket;
Esto significa: toma el rango de 0 a 100, cortalo en 10 partes iguales de anchura 10 cada una y devuelve el numero de tramo para score. Un valor en 0..10 cae en el cubo 1, 10..20 en el cubo 2, etcetera. El limite inferior se incluye y el superior se excluye.
La caracteristica clave son los dos cubos extra:
- si el valor es menor que el limite inferior, obtienes
0 (underflow);
- si el valor es igual o mayor que el limite superior, obtienes
n + 1 (overflow).
Asi nunca pierdes filas en silencio: todo lo que se sale del rango se apila ordenadamente en los bordes.
Distribuciones con GROUP BY
El uso mas habitual es un histograma de importes de pedido. Reparte amount de 0 a 1000 en 10 cubos y cuenta cada uno:
SELECT
WIDTH_BUCKET(amount, 0, 1000, 10) AS bucket,
COUNT(*) AS orders,
ROUND(MIN(amount), 2) AS lo,
ROUND(MAX(amount), 2) AS hi
FROM orders
WHERE status = 'paid'
GROUP BY bucket
ORDER BY bucket;
El cubo 0 recoge devoluciones e importes nulos, y el cubo 11 recoge los pedidos enormes por encima de 1000. Para convertir el numero en una etiqueta legible, calcula los limites dentro de la consulta:
SELECT
bucket,
(bucket - 1) * 100 AS range_lo,
bucket * 100 AS range_hi,
COUNT(*) AS orders
FROM (
SELECT WIDTH_BUCKET(amount, 0, 1000, 10) AS bucket
FROM orders
WHERE status = 'paid'
) t
GROUP BY bucket
ORDER BY bucket;
El mismo patron sirve para salarios: reparte employees.salary en bandas y veras al instante en que franjas se concentra la plantilla.
SELECT
WIDTH_BUCKET(salary, 30000, 150000, 6) AS band,
dept,
COUNT(*) AS headcount
FROM employees
GROUP BY band, dept
ORDER BY band, dept;
WIDTH_BUCKET frente a NTILE
Estas dos funciones se confunden con facilidad, pero dividen los datos en ejes distintos:
WIDTH_BUCKET corta el eje de valores en segmentos de igual anchura. Los cubos pueden quedar vacios o desbordados, y eso esta bien, porque la forma de la distribucion es justo lo que quieres ver.
NTILE(n) corta el conjunto de filas en n grupos de tamano parecido (cuantiles). La anchura de cada intervalo varia, pero cada grupo contiene casi la misma cantidad de filas.
Usa WIDTH_BUCKET cuando importan los umbrales fijos ("pedidos 0-100, 100-200..."). Usa NTILE cuando necesitas percentiles ("el 10% de clientes que mas gasta"):
SELECT
user_id,
SUM(amount) AS lifetime,
NTILE(4) OVER (ORDER BY SUM(amount)) AS quartile
FROM orders
GROUP BY user_id;
Trampa: limites y NULL
Algunos escollos en los que es facil tropezar:
- El limite superior se excluye. Exactamente
WIDTH_BUCKET(100, 0, 100, 10) devuelve 11, no 10. Para meter el maximo en el ultimo cubo real, amplia el limite superior o usa LEAST(amount, 999.99).
- Una entrada
NULL produce una salida NULL, asi que esas filas desaparecen de la distribucion. Filtralas de forma explicita o intagralas con COALESCE.
- Puedes pasar los limites al reves (superior < inferior), lo que invierte la numeracion y hace que los cubos cuenten hacia atras. Rara vez es lo que buscas, asi que revisa bien el orden.
Diferencias entre motores
- PostgreSQL y Oracle admiten directamente la forma de cuatro argumentos de
WIDTH_BUCKET.
- MySQL no tiene
WIDTH_BUCKET. Emulalo con aritmetica: FLOOR((amount - 0) / 100) + 1, y gestiona el underflow y el overflow a mano.
- ClickHouse ofrece
roundDown(amount, [0, 100, 200, ...]) y el simple floor(amount / 100) para el mismo efecto.
La regla de oro: fija min, max y el numero de cubos como umbrales de negocio deliberados en lugar de ajustarlos a los datos actuales, o dos informes vecinos dejaran de ser comparables.
WIDTH_BUCKETresponde a una pregunta sencilla: en que tramo del histograma cae un numero si dividimos un rango en partes de igual anchura. Es la herramienta ideal cuando necesitas agrupar un valor continuo (importe de un pedido, salario, puntuacion) en cubos uniformes y construir una distribucion.Firma y logica
La llamada canonica en PostgreSQL recibe cuatro argumentos:
SELECT WIDTH_BUCKET(score, 0, 100, 10) AS bucket; -- bucket 1..10Esto significa: toma el rango de
0a100, cortalo en10partes iguales de anchura10cada una y devuelve el numero de tramo parascore. Un valor en0..10cae en el cubo 1,10..20en el cubo 2, etcetera. El limite inferior se incluye y el superior se excluye.La caracteristica clave son los dos cubos extra:
0(underflow);n + 1(overflow).Asi nunca pierdes filas en silencio: todo lo que se sale del rango se apila ordenadamente en los bordes.
Distribuciones con GROUP BY
El uso mas habitual es un histograma de importes de pedido. Reparte
amountde 0 a 1000 en 10 cubos y cuenta cada uno:SELECT WIDTH_BUCKET(amount, 0, 1000, 10) AS bucket, COUNT(*) AS orders, ROUND(MIN(amount), 2) AS lo, ROUND(MAX(amount), 2) AS hi FROM orders WHERE status = 'paid' GROUP BY bucket ORDER BY bucket;El cubo
0recoge devoluciones e importes nulos, y el cubo11recoge los pedidos enormes por encima de 1000. Para convertir el numero en una etiqueta legible, calcula los limites dentro de la consulta:SELECT bucket, (bucket - 1) * 100 AS range_lo, bucket * 100 AS range_hi, COUNT(*) AS orders FROM ( SELECT WIDTH_BUCKET(amount, 0, 1000, 10) AS bucket FROM orders WHERE status = 'paid' ) t GROUP BY bucket ORDER BY bucket;El mismo patron sirve para salarios: reparte
employees.salaryen bandas y veras al instante en que franjas se concentra la plantilla.SELECT WIDTH_BUCKET(salary, 30000, 150000, 6) AS band, dept, COUNT(*) AS headcount FROM employees GROUP BY band, dept ORDER BY band, dept;WIDTH_BUCKET frente a NTILE
Estas dos funciones se confunden con facilidad, pero dividen los datos en ejes distintos:
WIDTH_BUCKETcorta el eje de valores en segmentos de igual anchura. Los cubos pueden quedar vacios o desbordados, y eso esta bien, porque la forma de la distribucion es justo lo que quieres ver.NTILE(n)corta el conjunto de filas enngrupos de tamano parecido (cuantiles). La anchura de cada intervalo varia, pero cada grupo contiene casi la misma cantidad de filas.Usa
WIDTH_BUCKETcuando importan los umbrales fijos ("pedidos 0-100, 100-200..."). UsaNTILEcuando necesitas percentiles ("el 10% de clientes que mas gasta"):SELECT user_id, SUM(amount) AS lifetime, NTILE(4) OVER (ORDER BY SUM(amount)) AS quartile FROM orders GROUP BY user_id;Trampa: limites y NULL
Algunos escollos en los que es facil tropezar:
WIDTH_BUCKET(100, 0, 100, 10)devuelve11, no10. Para meter el maximo en el ultimo cubo real, amplia el limite superior o usaLEAST(amount, 999.99).NULLproduce una salidaNULL, asi que esas filas desaparecen de la distribucion. Filtralas de forma explicita o intagralas conCOALESCE.Diferencias entre motores
WIDTH_BUCKET.WIDTH_BUCKET. Emulalo con aritmetica:FLOOR((amount - 0) / 100) + 1, y gestiona el underflow y el overflow a mano.roundDown(amount, [0, 100, 200, ...])y el simplefloor(amount / 100)para el mismo efecto.La regla de oro: fija
min,maxy el numero de cubos como umbrales de negocio deliberados en lugar de ajustarlos a los datos actuales, o dos informes vecinos dejaran de ser comparables.