NTILE(n) e uma funcao de janela que divide um conjunto ordenado de linhas em n baldes o mais iguais possivel em tamanho e marca cada linha com um numero de balde de 1 a n. E o cavalo de batalha por tras de quartis, decis e qualquer coorte de "numero igual de clientes por grupo".
O caso basico: quartis
Vamos dividir os usuarios em quatro grupos iguais pelo gasto total. ORDER BY ... DESC significa que o balde 1 e o "topo".
SELECT
u.id,
u.email,
SUM(o.amount) AS spent,
NTILE(4) OVER (ORDER BY SUM(o.amount) DESC) AS quartile
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.id, u.email;
A diferenca chave em relacao a RANK/ROW_NUMBER: o NTILE olha para o numero de linhas, nao para os valores concretos. Dez linhas com NTILE(4) produzem baldes de tamanho 3, 3, 2, 2 — a funcao arruma as linhas para voce.
Como o resto e distribuido
Quando o numero de linhas nao divide exato por n, as linhas extras vao para os baldes de numero menor. Para 10 linhas e NTILE(4): o resto e 10 mod 4 = 2, entao os dois primeiros baldes recebem uma linha extra cada.
- 10 linhas,
NTILE(4) → 3, 3, 2, 2
- 11 linhas,
NTILE(4) → 3, 3, 3, 2
- 7 linhas,
NTILE(3) → 3, 2, 2
Pegadinha: quando n e maior que o numero de linhas, alguns baldes nunca aparecem. Sete linhas com NTILE(10) produzem numeros de 1 a 7, e os baldes 8, 9, 10 ficam vazios. Nao assuma que todos os baldes existem.
Uma segunda armadilha: valores iguais (empates) ainda podem cair em baldes diferentes, porque se contam linhas, nao valores. Se o limite de uma coorte precisa coincidir com um limite de valor, o NTILE e a ferramenta errada — use WIDTH_BUCKET ou um CASE manual.
PARTITION BY: decis dentro de cada pais
PARTITION BY recalcula os baldes de forma independente dentro de cada grupo. Abaixo, decis de gasto calculados por pais.
SELECT
u.country,
u.email,
SUM(o.amount) AS spent,
NTILE(10) OVER (
PARTITION BY u.country
ORDER BY SUM(o.amount) DESC
) AS decile
FROM users u
JOIN orders o ON o.user_id = u.id
GROUP BY u.country, u.email;
Cada pais ganha sua propria escada de 1 a 10. A mesma ideia da decis de salario por departamento em uma unica consulta:
SELECT name, dept, salary,
NTILE(4) OVER (PARTITION BY dept ORDER BY salary DESC) AS pay_quartile
FROM employees;
NTILE versus WIDTH_BUCKET
E facil confundir os dois, mas a logica e diferente:
NTILE(n) iguala o numero de linhas entre os baldes. Cada balde tem mais ou menos o mesmo tamanho, mas as faixas de valor flutuam.
WIDTH_BUCKET(val, lo, hi, n) iguala a largura da faixa. Os baldes cobrem larguras de valor iguais, mas a contagem de linhas pode variar muito.
SELECT id, amount,
WIDTH_BUCKET(amount, 0, 1000, 4) AS price_band
FROM orders;
Regra pratica: "numero igual de clientes por grupo" → NTILE; "faixas de preco fixas" → WIDTH_BUCKET.
Compatibilidade
- PostgreSQL — suporta totalmente tanto
NTILE quanto WIDTH_BUCKET.
- MySQL —
NTILE existe desde a 8.0; nao ha WIDTH_BUCKET, entao ele e emulado com uma expressao FLOOR(...).
- ClickHouse — nao ha
NTILE nativo; use intDiv(rowNumberInAllBlocks(), ...) ou aritmetica sobre row_number().
O que importa lembrar: o NTILE corta por linhas, e o resto sempre pende para os baldes de numero menor — e isso que faz as coortes por quartis serem honestas.
NTILE(n)e uma funcao de janela que divide um conjunto ordenado de linhas emnbaldes o mais iguais possivel em tamanho e marca cada linha com um numero de balde de1an. E o cavalo de batalha por tras de quartis, decis e qualquer coorte de "numero igual de clientes por grupo".O caso basico: quartis
Vamos dividir os usuarios em quatro grupos iguais pelo gasto total.
ORDER BY ... DESCsignifica que o balde1e o "topo".SELECT u.id, u.email, SUM(o.amount) AS spent, NTILE(4) OVER (ORDER BY SUM(o.amount) DESC) AS quartile FROM users u JOIN orders o ON o.user_id = u.id WHERE o.status = 'paid' GROUP BY u.id, u.email;A diferenca chave em relacao a
RANK/ROW_NUMBER: oNTILEolha para o numero de linhas, nao para os valores concretos. Dez linhas comNTILE(4)produzem baldes de tamanho 3, 3, 2, 2 — a funcao arruma as linhas para voce.Como o resto e distribuido
Quando o numero de linhas nao divide exato por
n, as linhas extras vao para os baldes de numero menor. Para 10 linhas eNTILE(4): o resto e10 mod 4 = 2, entao os dois primeiros baldes recebem uma linha extra cada.NTILE(4)→ 3, 3, 2, 2NTILE(4)→ 3, 3, 3, 2NTILE(3)→ 3, 2, 2Uma segunda armadilha: valores iguais (empates) ainda podem cair em baldes diferentes, porque se contam linhas, nao valores. Se o limite de uma coorte precisa coincidir com um limite de valor, o
NTILEe a ferramenta errada — useWIDTH_BUCKETou umCASEmanual.PARTITION BY: decis dentro de cada pais
PARTITION BYrecalcula os baldes de forma independente dentro de cada grupo. Abaixo, decis de gasto calculados por pais.SELECT u.country, u.email, SUM(o.amount) AS spent, NTILE(10) OVER ( PARTITION BY u.country ORDER BY SUM(o.amount) DESC ) AS decile FROM users u JOIN orders o ON o.user_id = u.id GROUP BY u.country, u.email;Cada pais ganha sua propria escada de 1 a 10. A mesma ideia da decis de salario por departamento em uma unica consulta:
SELECT name, dept, salary, NTILE(4) OVER (PARTITION BY dept ORDER BY salary DESC) AS pay_quartile FROM employees;NTILE versus WIDTH_BUCKET
E facil confundir os dois, mas a logica e diferente:
NTILE(n)iguala o numero de linhas entre os baldes. Cada balde tem mais ou menos o mesmo tamanho, mas as faixas de valor flutuam.WIDTH_BUCKET(val, lo, hi, n)iguala a largura da faixa. Os baldes cobrem larguras de valor iguais, mas a contagem de linhas pode variar muito.-- Faixas de mesma LARGURA: 0-250, 250-500, 500-750, 750-1000 SELECT id, amount, WIDTH_BUCKET(amount, 0, 1000, 4) AS price_band FROM orders;Regra pratica: "numero igual de clientes por grupo" →
NTILE; "faixas de preco fixas" →WIDTH_BUCKET.Compatibilidade
NTILEquantoWIDTH_BUCKET.NTILEexiste desde a 8.0; nao haWIDTH_BUCKET, entao ele e emulado com uma expressaoFLOOR(...).NTILEnativo; useintDiv(rowNumberInAllBlocks(), ...)ou aritmetica sobrerow_number().O que importa lembrar: o
NTILEcorta por linhas, e o resto sempre pende para os baldes de numero menor — e isso que faz as coortes por quartis serem honestas.