在业务数据分析工作中,按指定维度分组后统计每组内不同业务类型的占比是高频需求,比如按地区分组统计各业务类型的订单占比,按部门分组统计各项目类型的工时占比。这类需求可以通过SQL的聚合函数配合分组语法高效实现,不需要额外导出数据到外部工具处理。

核心计算逻辑
分组内不同业务类型占比的计算本质是:某业务类型的记录数 / 该分组的总记录数 * 100%。要实现这个计算,首先需要用GROUP BY完成分组,再结合聚合函数分别获取分子分母的数值,最后做除法运算得到占比结果。
常用聚合函数适配方案
1. 通用方案:COUNT配合条件判断
这种方式兼容性最好,几乎所有关系型数据库都支持,核心是用CASE WHEN结合COUNT计算符合条件的记录数,再用COUNT(*)获取分组总记录数。
假设我们有订单表order_table,包含字段region(地区,分组维度)、business_type(业务类型),需要统计每个地区下不同业务类型的订单占比,示例代码如下:
-- 统计每个地区不同业务类型的订单占比
SELECT
region,
business_type,
-- 计算当前业务类型的订单数
COUNT(CASE WHEN business_type = t.business_type THEN 1 END) AS type_count,
-- 计算该地区总订单数
COUNT(*) AS total_count,
-- 计算占比,保留2位小数
ROUND(
COUNT(CASE WHEN business_type = t.business_type THEN 1 END) * 100.0 / COUNT(*),
2
) AS type_ratio
FROM order_table t
GROUP BY region, business_type
ORDER BY region, type_ratio DESC;
2. MySQL专用:SUM配合布尔值转换
MySQL中布尔表达式的结果会转换为1(真)和0(假),因此可以用SUM直接累加符合条件的记录数,代码更简洁。
-- MySQL环境统计分组内业务类型占比
SELECT
region,
business_type,
SUM(business_type = t.business_type) AS type_count,
COUNT(*) AS total_count,
ROUND(SUM(business_type = t.business_type) * 100.0 / COUNT(*), 2) AS type_ratio
FROM order_table t
GROUP BY region, business_type
ORDER BY region, type_ratio DESC;
3. 窗口函数方案:适配支持窗口函数的数据库
如果数据库支持窗口函数(如PostgreSQL、SQL Server、MySQL8.0+),可以用SUM OVER直接获取分组总记录数,不需要重复写聚合逻辑。
-- 窗口函数实现分组占比统计
SELECT DISTINCT
region,
business_type,
COUNT(*) OVER(PARTITION BY region, business_type) AS type_count,
COUNT(*) OVER(PARTITION BY region) AS total_count,
ROUND(
COUNT(*) OVER(PARTITION BY region, business_type) * 100.0 /
COUNT(*) OVER(PARTITION BY region),
2
) AS type_ratio
FROM order_table
ORDER BY region, type_ratio DESC;
注意事项
- 除法运算时建议给分子乘以100.0而不是100,避免整数除法导致结果取整错误,比如2/5在整数除法下结果为0,乘以100.0后结果为40.0。
- 如果分组内存在NULL值的业务类型,需要提前用
COALESCE函数处理,避免统计结果偏差。 - 不同数据库的四舍五入函数可能有差异,比如Oracle用
ROUND,SQL Server也用ROUND,用法基本一致,按需调整即可。
结果示例
以上查询的结果会类似如下表格:
| region | business_type | type_count | total_count | type_ratio |
|---|---|---|---|---|
| 华东 | 零售 | 120 | 300 | 40.00 |
| 华东 | 批发 | 90 | 300 | 30.00 |
| 华东 | 服务 | 90 | 300 | 30.00 |
| 华南 | 零售 | 150 | 400 | 37.50 |