累计分布与占总量百分比
计算分区内的累计百分比和总量占比
累计分布与占总量百分比 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
总计百分比问题
这是报表面试中很常见的问题:“每个类别的收入占总收入的百分比是多少?”以及与之对应的累计问题:“累计占总计的份额是多少?”
诀窍是用在整个分区上计算的窗口聚合值去除每一行的值。关键理解在于:您可以在窗口函数中得到总计,无需使用自连接,而这正是题目要考察的内容。
不使用 ORDER BY 的窗口 SUM = 总计
关键操作如下:SUM(amount) OVER () 使用空的 OVER 且不使用 ORDER BY,会返回整个结果集的总计,并在每一行重复显示。
由于没有 ORDER BY,因此不存在累计框架,默认框架就是整个分区。每行都显示总计,正是计算总计百分比所需的分母。
SELECT
category,
amount,
SUM(amount) OVER () AS grand_total
FROM category_sales;计算总计百分比
用每行的值除以窗口计算出的总计,再乘以 100。请转换为小数类型,以免整数除法将结果截断为零。
这个单次扫描查询取代了旧的做法:先通过子查询计算总计,再将总计连接回明细行。它更短、更快,读起来也更清晰。
SELECT
category,
amount,
ROUND(
100.0 * amount / SUM(amount) OVER (),
2
) AS pct_of_total
FROM category_sales;整数除法陷阱
这是一个经典的面试陷阱:在许多数据库中,对整数列执行 amount / total 会进行整数除法,因此小于 1 的结果会变成 0。
可以先乘以 100.0(一个数值字面量),或者转换其中一个操作数来修复:amount::numeric / total。忘记这一步会返回一列零值,面试官一眼就能发现。
SELECT
category,
amount * 1.0 / SUM(amount) OVER () AS share,
CAST(amount AS DECIMAL) / SUM(amount) OVER () AS share_alt
FROM category_sales;组内总计百分比
添加 PARTITION BY,让每行的占比相对于所属组,而不是整张表。例如,计算每个产品在其所属区域销售额中的百分比。
现在,分母 SUM(amount) OVER (PARTITION BY region) 会按区域重置,因此每个区域内的百分比总和为 100。
SELECT
region,
product,
amount,
ROUND(
100.0 * amount / SUM(amount) OVER (PARTITION BY region),
2
) AS pct_of_region
FROM regional_sales;累计总计百分比
将累计分子与固定分母结合起来,就能得到累计占总计的份额:也就是截至每一行,累计值占总计的多少。
分子使用 ORDER BY 进行累计计算,分母使用空的 OVER () 得到总计。最后一行始终会达到 100%。
SELECT
sale_date,
amount,
ROUND(
100.0 * SUM(amount) OVER (ORDER BY sale_date)
/ SUM(amount) OVER (),
2
) AS running_pct
FROM daily_sales;CUME_DIST:累计分布
SQL 内置了用于计算累计分布的函数:CUME_DIST()。它返回 ORDER BY 值小于或等于当前行的行数占比,结果范围为 (0, 1]。
与手动计算金额累计占比不同,CUME_DIST 关注的是行位置,回答的是“有多少比例的行的值小于或等于当前值?”它适合用于百分位数类报表。
SELECT
score,
CUME_DIST() OVER (ORDER BY score) AS cume_dist
FROM exam_results;PERCENT_RANK 及其区别
与之相近的是 PERCENT_RANK(),其定义为 (rank - 1) / (total_rows - 1),取值范围为 0 到 1。
面试中需要区分的是:CUME_DIST 将当前行计入分子(“小于或等于当前值”),而 PERCENT_RANK 表示相对排名,第一行从 0 开始。两者会产生不同的值,混淆它们是很常见的失误。
SELECT
score,
CUME_DIST() OVER (ORDER BY score) AS cd,
PERCENT_RANK() OVER (ORDER BY score) AS pr
FROM exam_results;帕累托 / 80-20 分析
累计总计百分比可以用于帕累托分析:“哪些排名靠前的客户贡献了 80% 的收入?”按金额降序排列,计算累计份额,然后筛选累计份额首次超过 80% 的位置。
由于窗口函数结果不能放在 WHERE 中,因此请将计算封装在 CTE 中,再在外层查询中进行筛选。这与所有窗口函数适用的规则相同。
WITH ranked AS (
SELECT
customer_id,
revenue,
SUM(revenue) OVER (ORDER BY revenue DESC)
/ SUM(revenue) OVER () AS running_share
FROM customer_revenue
)
SELECT *
FROM ranked
WHERE running_share <= 0.80;四舍五入与总和校正
注意:将每个百分比四舍五入到 2 位小数后,该列的总和可能变成 99.99 或 100.01,而不是恰好 100。面试官可能会问您如何保证各部分之和等于整体。
常见回答包括:仅在显示时进行四舍五入,计算时保留完整精度,或对某一行应用最大余数调整。能指出这个问题比说明修复方法更重要。
面试总结要点
需要口头说明的要点:
SUM(x) OVER ()不带 ORDER BY = 每行上的总计。- 乘以
100.0以避免整数除法。 - 使用
PARTITION BY计算每组占比。 - 累计分子除以总计分母 = 累计占比。
- 使用
CUME_DIST和PERCENT_RANK计算分布;您需要了解它们的差异。 - 使用 CTE 包装查询,以便进行帕累托分析或阈值筛选。
快速检查
如何让整个结果集的总计出现在每一行上?
回顾:分布与总计百分比
总计百分比是将某行的值除以 SUM(x) OVER ();后者会在每一行返回总计。请始终乘以 100.0 以避免整数除法,并添加 PARTITION BY 来计算每组占比。累计分子除以总计分母会得到累计占比,并最终达到 100%,这也是帕累托分析的基础。
对于基于位置的分布,请使用 CUME_DIST 和 PERCENT_RANK,并记住舍入与总和校正的注意事项。至此,累计总计和移动平均的内容就完成了。
常见问题解答
「累计分布与占总量百分比」课时是免费的吗?
是的 — 「累计分布与占总量百分比」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「累计分布与占总量百分比」这节课中我会学到什么?
计算分区内的累计百分比和总量占比 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「累计分布与占总量百分比」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用窗口帧计算累计和
- ROWS 与 RANGE 窗口帧
- 滑动窗口移动平均值
- 累计分布与占总量百分比