首页/数据/SQL 速查/占比计算(组内份额)
聚合与窗口函数54 条

占比计算(组内份额)

当前行值 ÷ 组内总和:用 SUM(...) OVER (PARTITION BY ...) 得到分母,一次扫描算出份额。

SQL 速查数据更新于 2026-10-13

组内占比怎么算

占比就是当前行的值除以所属分组(或全表)的合计。用窗口函数一步算出:SUM(amount) OVER (PARTITION BY 维度) 提供分母,与当前行的 amount 相除即可,不需要再写子查询或自连接去对齐分母。分母若是整表合计,直接去掉 PARTITION BY;若是上一层级(例如全国下的省份),要么用两级分区,要么先算出层级合计再取。除零和空值必须显式保护,否则 PostgreSQL 直接报错、MySQL 静默返回 NULL。

基本信息

适用平台MySQL 8.0+ / PostgreSQL 11+(窗口函数与嵌套聚合)
组内分母SUM(x) OVER (PARTITION BY 维度)
全表分母SUM(x) OVER (),不写 PARTITION BY
除零保护用 NULLIF(分母, 0),PostgreSQL 除零报错,MySQL 返回 NULL
类型细节PostgreSQL 整数相除会截断成整数,需先转 numeric;MySQL 的 / 返回十进制

典型使用场景

  • 各渠道销售额占总量的比例,用于结构分析
  • 各城市订单量占全国比例,直接按占比排序
  • 部门费用占公司总费用的份额
  • 单笔订单占该客户累计消费的比例
  • 与排名配合做 Pareto 分析(前 20% 客户贡献多少营收)

聚合行上的占比

聚合行上的占比
SELECT channel,
       SUM(amount) AS amount,
       ROUND(100 * SUM(amount) / SUM(SUM(amount)) OVER (), 2) AS pct_of_total
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY channel
ORDER BY pct_of_total DESC;

# 内层 SUM 完成分组聚合,外层窗口函数在全部分组上再求和

明细行上的占比

明细行上的占比
# 分母是该客户的全部消费,按明细行给出份额
SELECT o.id, o.user_id, o.amount,
       SUM(o.amount) OVER (PARTITION BY o.user_id) AS user_total,
       ROUND(100 * o.amount
             / NULLIF(SUM(o.amount) OVER (PARTITION BY o.user_id), 0), 2) AS pct_in_user
FROM orders o
WHERE o.status = 'paid'
ORDER BY o.user_id, o.id;

分母口径怎么选

目标分母写法注意点
占全表比例SUM(x) OVER ()不写 PARTITION BY
占本组比例SUM(x) OVER (PARTITION BY 维度)明细行与聚合行都可用
占上一层级比例两级分组或先用 CTE 算层级合计先确定层级口径再写分母
占比总和校验各组占比相加应接近 100%有排除条件或 NULL 时会对不上,属正常
嵌套聚合 SUM(SUM(x)) OVER () 在 MySQL 8 与 PostgreSQL 上都可用:内层完成分组,外层在全部分组上求和,比再写一个子查询求总量更简洁,也不会有对齐风险。
类型引发的精度问题必须确认:PostgreSQL 里整数相除会截断(3/4 得 0),要先转 numeric;除零在 PG 直接报错、在 MySQL 返回 NULL,两种都要用 NULLIF 显式保护,跨库脚本尤其要注意。

易错点

  • 分母为 0 或全为 NULL 时没有保护:PG 抛除零错误,MySQL 返回 NULL 让报表出现空值
  • PostgreSQL 里整数列直接相除被截断,占比整列都是 0
  • 分子分母层级不一致(分子是明细、分母是聚合),算出的占比大于 100%
  • 用 GROUP BY 之后又写子查询去对齐分母,语句复杂且口径容易对错,本可用窗口函数一步完成

数据来源与参考

  • MySQL 8.0 Reference Manual · Window Functions
  • PostgreSQL Documentation · Window Functions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌