SQL统计占比的核心是分子除以分母乘100,需确保分母准确(全局/分组/固定值),处理NULL、整数除法和小数精度;常用COUNT(CASE WHEN...)、窗口函数、NULLIF及浮点强制转换。

SQL中统计占比(即百分比)的核心思路是:用某部分的数值除以总体数值,再乘以100,并注意处理小数精度和NULL值。关键不在函数多复杂,而在分母是否准确、是否需要全局总计或分组内总计。
基础写法:单个指标占总记录数的百分比
适用于计算满足某条件的记录占全表比例,例如“订单中已支付订单占比”:
SELECT
ROUND(
COUNT(CASE WHEN status = 'paid' THEN 1 END) * 100.0 / COUNT(*),
2
) AS paid_ratio
FROM orders;
- 用
COUNT(CASE WHEN ...)统计分子,避免COUNT(*)忽略NULL导致偏差 - 分母用
COUNT(*)确保包含所有行(包括status为NULL的记录) -
* 100.0强制转为浮点数,防止整数除法截断(尤其在MySQL/PostgreSQL中) -
ROUND(..., 2)保留两位小数,更符合业务展示习惯
分组内占比:每个类别的数据占本组的百分比
例如“各城市中,不同订单状态的占比”——需用窗口函数实现组内归一化:
SELECT
city,
status,
COUNT(*) AS cnt,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY city), 2) AS ratio_in_city
FROM orders
GROUP BY city, status;
-
SUM(COUNT(*)) OVER (PARTITION BY city)先按city分组聚合,再对每组求和,得到每个城市的总订单数 - 窗口函数必须配合GROUP BY使用,不能直接在SELECT中嵌套普通聚合
- 若想按状态汇总后再算城市占比,需调整PARTITION BY字段(如改为
PARTITION BY status)
与固定总数对比的占比(如目标完成率)
当分母是外部给定值(如销售目标1000万元),可用子查询或WITH定义:
WITH target AS (SELECT 1000.0 AS amount)
SELECT
ROUND(SUM(amount) * 100.0 / (SELECT amount FROM target), 2) AS completion_rate
FROM sales;
- 用CTE或子查询明确表达分母来源,提升可读性与复用性
- 注意类型一致:若目标值是整数,建议写成
1000.0避免整除问题 - 生产环境中建议将目标值存在配置表中,用JOIN替代硬编码
规避常见错误
以下写法容易出错,需特别注意:
-
分母为0未处理:加
NULLIF(denominator, 0)防止报错,如num * 100.0 / NULLIF(total, 0) - 忽略NULL值影响计数:COUNT(col)会跳过NULL,而COUNT(*)不会;按需选择
-
没转浮点数导致结果为0:在SQL Server/MySQL中
5 / 8结果是0,必须写成5.0 / 8或CAST(5 AS FLOAT) / 8 -
ORDER BY后才计算占比:窗口函数不受ORDER BY影响,但若用ROW_NUMBER等排序相关函数,需确认是否需要
ORDER BY子句










