使用窗口帧计算累计和
使用带有排序窗口帧的 SUM OVER 构建运行总计
使用窗口帧计算累计和 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
累计总额问题
几乎每场分析师面试都会以某种形式提出这样的问题:“请显示随时间变化的累计收入。” 累计总额是一种逐行增长的求和,从开头累积到当前行的所有内容。
在窗口函数出现之前,候选人会用缓慢的自连接或相关子查询来解决这个问题。如今标准答案是 SUM(...) OVER (ORDER BY ...)。了解窗口框架版本的写法,说明您理解 2012 年左右之后编写的 SQL。
有序窗口求和的组成
累计总额只是将聚合函数转换为窗口函数。您保留 SUM(amount),但添加带有 ORDER BY 的 OVER 子句。
OVER 内的 ORDER BY 正是使结果具有累计性质的原因:它告诉 SQL 按该顺序累积各行。如果没有 ORDER BY,SUM 就会为每一行计算整个分区的总和,而不是逐步增长。
SELECT
sale_date,
amount,
SUM(amount) OVER (ORDER BY sale_date) AS running_total
FROM sales
ORDER BY sale_date;ORDER BY 为什么会隐含框架
这是面试官喜欢深入追问的细节:当您向窗口聚合函数添加 ORDER BY 时,SQL 会应用一个默认框架:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。
这个默认设置正是产生累计总额的原因:从分区开头到当前行(包括当前行)的每一行都会被累积。理解这个默认设置,您就理解了累计求和为什么会自动生效。
明确指定框架
您可以手动写出框架。这两个查询返回相同的结果,但明确指定框架的版本能向面试官表明,您知道底层发生了什么。
对于累计总额,写出 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 是最稳妥的明确形式,因为它按物理行计数,避免了 RANGE 按值分组可能带来的意外(下一课会讲到)。
SELECT
sale_date,
amount,
SUM(amount) OVER (
ORDER BY sale_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM sales;实战示例:每日销售额
设想四天的销售额:周一 100,周二 50,周三 200,周四 75。累计总额从左到右逐步累积。
- 周一:100
- 周二:100 + 50 = 150
- 周三:150 + 200 = 350
- 周四:350 + 75 = 425
最后一行始终等于总计。这是您可以在面试中提到的快速合理性检查:最后一个累计总额必须与整个数据集上的 SUM(amount) 相等。
使用 PARTITION BY 逐组重置
实际问题通常要求按客户或地区计算累计总额,而不是计算一个全局总额。添加 PARTITION BY 后,每个分区开头都会重新开始累积。
可以这样理解:PARTITION BY 将各行分成相互独立的分组,而 ORDER BY 和框架会在每个分组内分别运行。
SELECT
customer_id,
sale_date,
amount,
SUM(amount) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS customer_running_total
FROM sales;次级排序键陷阱
如果两行具有相同的 ORDER BY 值(例如同一天的两笔销售),默认的 RANGE 框架会将它们视为同值行,并为它们提供相同的累计总额,其中包括两笔金额。
如果您需要即使存在相同值也严格按行递增,请改用 ROWS 框架,并在 ORDER BY 中添加唯一的次级排序键,例如 sale_date, id。面试官会特意设置重复日期,观察您是否注意到这一点。
SELECT
sale_date,
amount,
SUM(amount) OVER (
ORDER BY sale_date, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM sales;计数的累计值
累计逻辑并不局限于 SUM。任何聚合函数都可以作为窗口函数使用,因此您可以构建累计计数、累计平均值或累计最大值。
订单的累计计数是仪表板中常见的指标:截至每天为止,我们目前接收了多少订单?
SELECT
order_date,
COUNT(*) OVER (
ORDER BY order_date
) AS orders_to_date
FROM orders;旧方法:相关子查询
面试官有时会要求您在不使用窗口函数的情况下解决累计总额问题,以测试您的理解深度。窗口函数出现以前的经典解决方案是相关子查询,它会重新计算之前每一行的总和。
这种方法可以工作,但复杂度为 O(n²):它会针对每一行重新扫描数据表。请提到这一点,表明您知道窗口函数为何取代了这种方法。
SELECT
s.sale_date,
s.amount,
(SELECT SUM(s2.amount)
FROM sales s2
WHERE s2.sale_date <= s.sale_date) AS running_total
FROM sales s
ORDER BY s.sale_date;过滤与窗口结果
一个常见的后续问题是:“只显示累计总额超过 1000 的日期。”您不能将窗口函数放入 WHERE 中,因为框架是在 WHERE 执行之后才计算的。
修复方法是在 CTE 或子查询中计算累计总额,然后在外层查询中过滤。这是适用于每个窗口函数的相同外层包裹规则。
WITH t AS (
SELECT
sale_date,
SUM(amount) OVER (ORDER BY sale_date) AS running_total
FROM sales
)
SELECT *
FROM t
WHERE running_total >= 1000;面试要点
回答累计总额问题时,请说明以下几点,以获得满分:
SUM OVER (ORDER BY ...)是累计形式。- 添加
ORDER BY会创建从UNBOUNDED PRECEDING到CURRENT ROW的默认框架。 - 使用
PARTITION BY按分组重置。 - 添加唯一的次级排序键,并使用
ROWS框架,以避免重复值陷阱。 - 将查询包裹在 CTE 中,以便按结果进行过滤。
快速检查
请检验您对默认框架的理解。
回顾:累计求和
累计总额是一种有序窗口聚合。SUM(amount) OVER (ORDER BY sale_date) 会从分区开头累积到当前行,这得益于隐含的 UNBOUNDED PRECEDING 到 CURRENT ROW 框架。
使用 PARTITION BY 按分组重置;添加次级排序键和 ROWS 框架来处理重复的排序值;当您需要按累计值进行过滤时,将查询包裹在 CTE 中。接下来,我们将详细分析本课提到的 ROWS 与 RANGE 之间的区别。
常见问题解答
「使用窗口帧计算累计和」课时是免费的吗?
是的 — 「使用窗口帧计算累计和」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「使用窗口帧计算累计和」这节课中我会学到什么?
使用带有排序窗口帧的 SUM OVER 构建运行总计 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「使用窗口帧计算累计和」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用窗口帧计算累计和
- ROWS 与 RANGE 窗口帧
- 滑动窗口移动平均值
- 累计分布与占总量百分比