0Pricing
SQL Interview Prep · 课时

累计分布与占总量百分比

计算分区内的累计百分比和总量占比

累计分布与占总量百分比 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL 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 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。

「累计分布与占总量百分比」这节课中我会学到什么?

计算分区内的累计百分比和总量占比 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 SQL Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「累计分布与占总量百分比」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 SQL Interview Prep 课中编写并运行代码吗?

能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用窗口帧计算累计和
  2. ROWS 与 RANGE 窗口帧
  3. 滑动窗口移动平均值
  4. 累计分布与占总量百分比
← 返回 SQL Interview Prep