分桶计算是在GROUP BY结果上对连续字段按等宽或等频划分区间并统计,区别于GROUP BY的离散分类;常用NTILE()实现等频分桶、WIDTH_BUCKET()或手动公式实现等宽分桶。

什么是分桶计算,和 GROUP BY 有什么区别?
分桶计算不是 SQL 标准语法里的术语,实际指的是:在 GROUP BY 结果基础上,把每个分组再按某个连续字段(比如销售额、年龄、时间)切分成若干等宽或等频的“桶”,然后统计每桶内有多少组、或每桶的聚合值。它和单纯 GROUP BY 的关键差异在于——GROUP BY 是离散分类(如按 category 分),而分桶是连续域上的区间划分(如把 price 每 100 元一段)。
常见误操作是试图用 GROUP BY 直接套用 CASE WHEN 写一堆区间,但逻辑松散、不易复用;更稳妥的方式是先构造桶边界,再 JOIN 或子查询映射。
用 NTILE() 实现等频分桶(每桶行数尽量相等)
NTILE(n) 是窗口函数,能把结果集平均分成 n 桶,编号从 1 到 n。注意:它作用于 整个结果集,不是每个分组内部——所以若你已用 GROUP BY 得到汇总行(比如每个城市一个 SUM(sales)),得先把这些汇总行作为子查询,再对其应用 NTILE。
-
NTILE(4)对 10 行数据会分配为:3,3,2,2(不是严格均分,但尽量均衡) - 它不关心数值大小,只看排序后的位置,适合做“Top 25% / Median / Bottom 25%”类分析
- 若需按分组内分桶(比如每个省份内销量分四档),必须加
PARTITION BY province
SELECT province, total_sales,
NTILE(4) OVER (PARTITION BY province ORDER BY total_sales) AS sales_quartile
FROM (SELECT province, SUM(sales) AS total_sales
FROM orders GROUP BY province) t;用 WIDTH_BUCKET() 做等宽分桶(按数值范围切固定宽度区间)
WIDTH_BUCKET(value, min, max, bucket_count) 是 Oracle/PostgreSQL 支持的函数(MySQL 不原生支持),直接返回 value 落在哪一个等宽桶里。它真正实现“按数值分桶”,且天然支持分组后二次聚合。
- 第二、三参数必须是常量或确定值;如果
min/max来自数据本身(比如全表MIN(price)),得用子查询或 CTE 预先算出 - 桶编号从 1 开始,超出
[min, max]范围的值会返回 0 或bucket_count+1(取决于数据库),务必检查边界处理 - 示例中把各品类平均价格分成 5 档:
WITH price_stats AS (
SELECT category, AVG(price) AS avg_price,
MIN(AVG(price)) OVER() AS global_min,
MAX(AVG(price)) OVER() AS global_max
FROM products GROUP BY category
)
SELECT category, avg_price,
WIDTH_BUCKET(avg_price, global_min, global_max, 5) AS price_bucket
FROM price_stats;MySQL 用户怎么绕过缺失的 WIDTH_BUCKET?
MySQL 8.0+ 没有 WIDTH_BUCKET(),但可以用 FLOOR((value - min_val) / bucket_width) + 1 手动模拟等宽桶:
- 必须提前知道全局
min和max,否则无法统一桶宽 -
bucket_width = (max - min) / n,注意浮点精度可能导致边界错位,建议用ROUND(..., 2)控制小数位 - 更安全的做法是用
UNION ALL构建桶定义表,再LEFT JOIN映射,尤其当桶边界非线性时(比如 [0–100), [100–500), [500–∞))
SELECT t.category, t.avg_price, b.bucket_id FROM ( SELECT category, AVG(price) AS avg_price FROM products GROUP BY category ) t LEFT JOIN ( SELECT 1 AS bucket_id, 0 AS low, 100 AS high UNION ALL SELECT 2, 100, 500 UNION ALL SELECT 3, 500, 999999 ) b ON t.avg_price >= b.low AND t.avg_price < b.high;
分桶真正的复杂点不在写法,而在“桶的语义是否对齐业务目标”——等频桶可能让一个桶里混着高价低毛利和低价高毛利商品;等宽桶在数据偏斜时会导致某些桶空、某些桶挤爆。动手前先确认:你要的是分布形状,还是可解释的业务区间。

















