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 <= 1000
的人。
SELECT *
FROM (SELECT person_name,turn,SUM(weight) OVER (ORDER BY turn) AS cumulative_weightFROM Queue
) temp
WHERE cumulative_weight <= 1000;
✅ 3.4 找出 turn 最大的那个(最后能上车的人)
在满足 总重 ≤ 1000
的人里,最后上车的就是 turn 最大的那位。
ORDER BY turn DESC
LIMIT 1
🔄 这段 SQL 的“运行流程”是这样:
-
内部 SELECT(临时表 temp):
-
按照上车顺序,为每个人计算自己+前面所有人的累积体重。
-
-
中间 WHERE 过滤:
-
删除累积重量超过 1000 的人,保留能上车的。
-
-
最外层处理:
-
按 turn 倒序(最大的 turn 最前),只取第一条,也就是“最后一个能上车的人的名字”。
-
最终 SQL 解法
SELECT person_name
FROM (SELECT person_name,SUM(weight) OVER (ORDER BY turn) AS cumulative_weight,turnFROM Queue
) AS temp
WHERE cumulative_weight <= 1000
ORDER BY turn DESC
LIMIT 1;