SQL思路解析:窗口函数该如何使用?
目录
🔍 Step 1:我们知道什么?
🧩 Step 2:我们要构建什么?(问题建模)
🔧 Step 3:我们如何实现它?(技术路径)
✅ 3.1 按照 turn 排序,逐个考虑
✅ 3.2 对每一个人,我们想知道“上车时的总重”
✅ 3.3 筛选哪些人可以上车
✅ 3.4 找出 turn 最大的那个(最后能上车的人)
🔄 这段 SQL 的“运行流程”是这样:
最终 SQL 解法
表: Queue +-------------+---------+ | Column Name | Type | +-------------+---------+ | person_id | int | | person_name | varchar | | weight | int | | turn | int | +-------------+---------+ person_id 是这个表具有唯一值的列。 该表展示了所有候车乘客的信息。 表中 person_id 和 turn 列将包含从 1 到 n 的所有数字,其中 n 是表中的行数。 turn 决定了候车乘客上巴士的顺序,其中 turn=1 表示第一个上巴士,turn=n 表示最后一个上巴士。 weight 表示候车乘客的体重,以千克为单位。
有一队乘客在等着上巴士。然而,巴士有1000 千克 的重量限制,所以其中一部分乘客可能无法上巴士。
编写解决方案找出 最后一个 上巴士且不超过重量限制的乘客,并报告 person_name 。题目测试用例确保顺位第一的人可以上巴士且不会超重。
返回结果格式如下所示。
示例 1: 输入: Queue 表 +-----------+-------------+--------+------+ | person_id | person_name | weight | turn | +-----------+-------------+--------+------+ | 5 | Alice | 250 | 1 | | 4 | Bob | 175 | 5 | | 3 | Alex | 350 | 2 | | 6 | John Cena | 400 | 3 | | 1 | Winston | 500 | 6 | | 2 | Marie | 200 | 4 | +-----------+-------------+--------+------+ 输出: +-------------+ | person_name | +-------------+ | John Cena | +-------------+ 解释: 为了简化,Queue 表按 turn 列由小到大排序。 +------+----+-----------+--------+--------------+ | Turn | ID | Name | Weight | Total Weight | +------+----+-----------+--------+--------------+ | 1 | 5 | Alice | 250 | 250 | | 2 | 3 | Alex | 350 | 600 | | 3 | 6 | John Cena | 400 | 1000 | (最后一个上巴士) | 4 | 2 | Marie | 200 | 1200 | (无法上巴士) | 5 | 4 | Bob | 175 | ___ | | 6 | 1 | Winston | 500 | ___ | +------+----+-----------+--------+--------------+
来源:Leecode
🔍 Step 1:我们知道什么?
-
所有人都有唯一的上车顺序(turn)。
(图片来源网络,侵删) -
上车顺序已知,即我们可以将人按 turn ASC 排序。
-
每个人有体重。巴士的总承重上限是 1000kg。
(图片来源网络,侵删) -
从第一个人开始累加,如果加上某个人会超过 1000kg,则此人不能上,前面的人都上了。
🧩 Step 2:我们要构建什么?(问题建模)
我们想要解决的是:
(图片来源网络,侵删)在 turn 排序下,找出满足 累积体重 ≤ 1000kg 的 最大 turn 的人。
这个问题实质上是个“顺序累加 + 截断”的问题。
🧠 核心思想:
-
从第一个人开始(turn=1),累计体重;
-
当某人加进去刚好等于或不超过1000,就暂时保留;
-
一旦某人加进去会导致总重超过1000,我们就停止,返回上一个能上车的人。
🔧 Step 3:我们如何实现它?(技术路径)
接下来我们从本质逐步推导 SQL 解法。
✅ 3.1 按照 turn 排序,逐个考虑
首先我们知道人要按 turn 排序:
SELECT * FROM Queue ORDER BY turn;
✅ 3.2 对每一个人,我们想知道“上车时的总重”
这时我们有了一个自然的问题:
在某个人 turn 时,上车总重是多少?
这就引出了窗口函数的使用 —— 我们希望每一行“看到前面的人的体重总和”。
🎯 这里 SUM(...) OVER (...) 的意思是:
对当前人以及比他更早上车的人的体重累加。
这样我们就知道了:每个人上车时的总重量。
SELECT person_name, turn, weight, SUM(weight) OVER (ORDER BY turn) AS cumulative_weight FROM Queue;
这一行在做的是:
每个乘客“看到自己和自己前面人”所有体重的累加。
补充:为什么不能用普通 GROUP BY 或子查询替代?
很多同学会想:我用一个变量手动累加不就可以了?
但在 SQL 中,每行是独立执行的,你不能在一行里“记住上一行”的累计除非你用:
-
窗口函数 ✅
-
临时变量(不推荐,且部分 SQL 不支持)
✅ 3.3 筛选哪些人可以上车
下一步逻辑就是:
只保留 cumulative_weight
-