用GROUP BY+窗口函数算占比最稳,因SUM(COUNT()) OVER()能跨组获取全表总数,避免整数截断和除零错误,正确写法为ROUND(COUNT() 100.0 / SUM(COUNT()) OVER(), 2)。

用 GROUP BY + 窗口函数算占比最稳
直接在聚合后除以总数容易出错,因为分母必须是全表总和,不是当前组的聚合值。窗口函数 SUM() OVER() 能跨组拿到总数,是唯一可靠方式。
常见错误是写成 COUNT(*) / COUNT(*) 或用子查询反复查总数——前者结果全是 1,后者性能差且易被优化器搞乱。
- 正确写法:
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) -
* 100.0强制转浮点,避免整数除法截断(尤其在 PostgreSQL/SQL Server 中) -
ROUND(..., 2)控制小数位,但注意:四舍五入可能导致加总不等于 100
MySQL 8.0+ 和旧版处理方式不同
MySQL 8.0 支持窗口函数,写法同上;但 5.7 及更早版本不支持 OVER(),只能用 JOIN 或变量模拟。
旧版常用方案是先查总数再关联,比如:
SELECT t1.category, COUNT(*) AS cnt,
ROUND(COUNT(*) * 100.0 / t2.total, 2) AS pct
FROM orders t1
CROSS JOIN (SELECT COUNT(*) AS total FROM orders) t2
GROUP BY t1.category;
注意:CROSS JOIN 在大数据量时比窗口函数慢,且不能用索引加速聚合。
NULL 值会让百分比计算变零或报错
如果分组字段含 NULL,GROUP BY 会把它们单独归为一组,但用户常误以为这些数据“没被统计”。更危险的是,若用 SUM(column) 当分母而该列有 NULL,结果可能为 NULL,导致整个占比为 NULL。
- 检查分母是否为空:
NULLIF(SUM(COUNT(*)) OVER(), 0)避免除零 - 显式过滤或归类
NULL:GROUP BY COALESCE(category, 'Unknown') - 别依赖
WHERE column IS NOT NULL后再算占比——这会改变总数基准,结果失真
百分比加总不等于 100 是正常现象
这不是 SQL 写错了,而是四舍五入的数学必然。比如三组占比分别是 33.33%、33.33%、33.34%,加起来是 100.00%;但如果都保留 1 位小数,就变成 33.3 + 33.3 + 33.3 = 99.9。
业务系统里常需“补足 100”,但 SQL 层不适合硬补——它掩盖了真实分布。真要对齐,得在应用层做最后一位调整,或者改用整数百分比(如万分比)再统一缩放。










