WIDTH_BUCKET responde a uma pergunta simples: em qual faixa do histograma um numero cai se dividirmos um intervalo em partes de largura igual. E a ferramenta certa sempre que voce precisa agrupar um valor continuo (valor do pedido, salario, pontuacao) em baldes uniformes e montar uma distribuicao.
Assinatura e logica
A chamada canonica no PostgreSQL recebe quatro argumentos:
SELECT WIDTH_BUCKET(score, 0, 100, 10) AS bucket;
Isso significa: pegue o intervalo de 0 a 100, corte em 10 partes iguais de largura 10 cada uma e retorne o numero da faixa para score. Um valor em 0..10 cai no balde 1, 10..20 no balde 2, e assim por diante. O limite inferior e inclusivo e o superior e exclusivo.
O recurso central sao os dois baldes extras:
- se o valor for menor que o limite inferior, voce recebe
0 (underflow);
- se o valor for igual ou maior que o limite superior, voce recebe
n + 1 (overflow).
Voce nunca perde linhas em silencio: tudo que fica fora do intervalo se acumula direitinho nas bordas.
O uso mais comum e um histograma dos valores de pedido. Divida amount de 0 a 1000 em 10 baldes e conte cada um:
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;
O balde 0 junta estornos e valores zerados, e o balde 11 junta os pedidos gigantes acima de 1000. Para transformar o numero em um rotulo legivel, calcule os limites dentro da propria 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;
O mesmo padrao serve para salarios: distribua employees.salary em faixas e veja na hora onde fica a maior parte do quadro de pessoal.
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 versus NTILE
As duas funcoes sao faceis de confundir, mas dividem os dados em eixos diferentes:
WIDTH_BUCKET corta o eixo de valores em segmentos de largura igual. Os baldes podem ficar vazios ou cheios demais, e isso e normal, porque o formato da distribuicao e exatamente o que voce quer enxergar.
NTILE(n) corta o conjunto de linhas em n grupos de tamanho parecido (quantis). A largura de cada intervalo varia, mas cada grupo guarda quase a mesma quantidade de linhas.
Use WIDTH_BUCKET quando os limites fixos importam ("pedidos 0-100, 100-200..."). Use NTILE quando precisa de percentis ("os 10% de clientes que mais gastam"):
SELECT
user_id,
SUM(amount) AS lifetime,
NTILE(4) OVER (ORDER BY SUM(amount)) AS quartile
FROM orders
GROUP BY user_id;
Pegadinha: limites e NULL
Algumas armadilhas em que e facil escorregar:
- O limite superior e exclusivo. Exatamente
WIDTH_BUCKET(100, 0, 100, 10) retorna 11, e nao 10. Para puxar o maximo para o ultimo balde real, amplie o limite superior ou use LEAST(amount, 999.99).
- Uma entrada
NULL gera uma saida NULL, entao essas linhas somem da distribuicao. Filtre-as explicitamente ou incorpore-as com COALESCE.
- Voce pode passar os limites invertidos (superior < inferior), o que inverte a numeracao e faz os baldes contarem para tras. Isso raramente e o que voce quer, entao confira bem a ordem.
Diferencas entre os bancos
- PostgreSQL e Oracle suportam diretamente a forma de quatro argumentos do
WIDTH_BUCKET.
- MySQL nao tem
WIDTH_BUCKET. Emule com aritmetica: FLOOR((amount - 0) / 100) + 1, e trate underflow e overflow na mao.
- ClickHouse oferece
roundDown(amount, [0, 100, 200, ...]) e o simples floor(amount / 100) para o mesmo efeito.
A regra de ouro: fixe min, max e a quantidade de baldes como limites de negocio deliberados em vez de ajusta-los aos dados atuais, senao dois relatorios vizinhos deixam de ser comparaveis.
WIDTH_BUCKETresponde a uma pergunta simples: em qual faixa do histograma um numero cai se dividirmos um intervalo em partes de largura igual. E a ferramenta certa sempre que voce precisa agrupar um valor continuo (valor do pedido, salario, pontuacao) em baldes uniformes e montar uma distribuicao.Assinatura e logica
A chamada canonica no PostgreSQL recebe quatro argumentos:
SELECT WIDTH_BUCKET(score, 0, 100, 10) AS bucket; -- bucket 1..10Isso significa: pegue o intervalo de
0a100, corte em10partes iguais de largura10cada uma e retorne o numero da faixa parascore. Um valor em0..10cai no balde 1,10..20no balde 2, e assim por diante. O limite inferior e inclusivo e o superior e exclusivo.O recurso central sao os dois baldes extras:
0(underflow);n + 1(overflow).Voce nunca perde linhas em silencio: tudo que fica fora do intervalo se acumula direitinho nas bordas.
Distribuicoes com GROUP BY
O uso mais comum e um histograma dos valores de pedido. Divida
amountde 0 a 1000 em 10 baldes e conte cada um: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;O balde
0junta estornos e valores zerados, e o balde11junta os pedidos gigantes acima de 1000. Para transformar o numero em um rotulo legivel, calcule os limites dentro da propria 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;O mesmo padrao serve para salarios: distribua
employees.salaryem faixas e veja na hora onde fica a maior parte do quadro de pessoal.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 versus NTILE
As duas funcoes sao faceis de confundir, mas dividem os dados em eixos diferentes:
WIDTH_BUCKETcorta o eixo de valores em segmentos de largura igual. Os baldes podem ficar vazios ou cheios demais, e isso e normal, porque o formato da distribuicao e exatamente o que voce quer enxergar.NTILE(n)corta o conjunto de linhas emngrupos de tamanho parecido (quantis). A largura de cada intervalo varia, mas cada grupo guarda quase a mesma quantidade de linhas.Use
WIDTH_BUCKETquando os limites fixos importam ("pedidos 0-100, 100-200..."). UseNTILEquando precisa de percentis ("os 10% de clientes que mais gastam"):SELECT user_id, SUM(amount) AS lifetime, NTILE(4) OVER (ORDER BY SUM(amount)) AS quartile FROM orders GROUP BY user_id;Pegadinha: limites e NULL
Algumas armadilhas em que e facil escorregar:
WIDTH_BUCKET(100, 0, 100, 10)retorna11, e nao10. Para puxar o maximo para o ultimo balde real, amplie o limite superior ou useLEAST(amount, 999.99).NULLgera uma saidaNULL, entao essas linhas somem da distribuicao. Filtre-as explicitamente ou incorpore-as comCOALESCE.Diferencas entre os bancos
WIDTH_BUCKET.WIDTH_BUCKET. Emule com aritmetica:FLOOR((amount - 0) / 100) + 1, e trate underflow e overflow na mao.roundDown(amount, [0, 100, 200, ...])e o simplesfloor(amount / 100)para o mesmo efeito.A regra de ouro: fixe
min,maxe a quantidade de baldes como limites de negocio deliberados em vez de ajusta-los aos dados atuais, senao dois relatorios vizinhos deixam de ser comparaveis.