1204. 最后一个能进入巴士的人
地址
题目
表: 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 千克 的重量限制,所以其中一部分乘客可能无法上巴士。
写一条 SQL 查询语句找出 最后一个 上巴士且不超过重量限制的乘客,并报告 person_name 。题目测试用例确保顺位第一的人可以上巴士且不会超重。
查询结果格式如下所示。
示例
输入:
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 |
---|
Thomas Jefferson |
解释:
为了简化,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 | ___ |
解题
方法一: 窗口函数
- 在窗口函数中求出所有的累加和
select * ,sum(weight) over(order by turn) as sumweight from Queue
此时结果
person_id | person_name | weight | turn | sumweight |
---|---|---|---|---|
5 | Alice | 250 | 1 | 250 |
3 | Alex | 350 | 2 | 600 |
6 | John Cena | 400 | 3 | 1000 |
2 | Marie | 200 | 4 | 1200 |
4 | Bob | 175 | 5 | 1375 |
1 | Winston | 500 | 6 | 1875 |
所以此时倒序排序找出sumweight小于等于1000 的对应的person id 即为最后结果
- 此时最终语句为
select person_name from (
select * ,sum(weight) over(order by turn) as sumweight from Queue
) as a
where sumweight<=1000
order by sumweight desc
limit 1
最终结果
person_name |
---|
Thomas Jefferson |
解释
我们没有对窗口函数进行范围限制 默认为 从开始到轮到的位置 求和 即为前一总和,前二总和 , 前三总和。。。
方法二: 子查询
- 子查询求前n和
select sum(weight) from Queue
where turn <=n
此时结果
为单个前n项的总和
- 此时在外侧嵌套一层查询
select *
from Queue q1
where (select sum(weight) from Queue where turn <= q1.turn) <= 1000
此时结果
person_id | person_name | weight | turn |
---|---|---|---|
5 | Alice | 250 | 1 |
3 | Alex | 350 | 2 |
6 | John Cena | 400 | 3 |
解释
对于限制条件**<= q1.turn**
- 语句开始时 q1.turn=1
-
- 子查询语句求turn<=1的总和
-
- 满足小于等于1000
-
- 此时turn=1 被选出来
- q1.turn=2
-
- 子查询语句求turn<=2的总和
-
- 满足小于等于1000
-
- 此时turn=2 被选出来
- 以此类推
-
- q1.turn=4
-
- 子查询语句求turn<=4的总和
-
- 不满足小于等于1000
-
- 此时turn=4 没被选出来
- 以此类推 得到结果
最后我们对查出的结果按turn倒序排序取第一个即为最后结果
方法三:内联查询
- 找出符合条件的每一列数据 根据a.turn>b.turn 得出 每个人进入时的所有人情况
select * from Queue a , Queue b
where a.turn>=b.turn
order by a.turn
此时结果
person_id | person_name | weight | turn | person_id | person_name | weight | turn |
---|---|---|---|---|---|---|---|
5 | Alice | 250 | 1 | 5 | Alice | 250 | 1 |
3 | Alex | 350 | 2 | 5 | Alice | 250 | 1 |
3 | Alex | 350 | 2 | 3 | Alex | 350 | 2 |
6 | John Cena | 400 | 3 | 5 | Alice | 250 | 1 |
6 | John Cena | 400 | 3 | 6 | John Cena | 400 | 3 |
6 | John Cena | 400 | 3 | 3 | Alex | 350 | 2 |
2 | Marie | 200 | 4 | 5 | Alice | 250 | 1 |
2 | Marie | 200 | 4 | 2 | Marie | 200 | 4 |
2 | Marie | 200 | 4 | 3 | Alex | 350 | 2 |
2 | Marie | 200 | 4 | 6 | John Cena | 400 | 3 |
4 | Bob | 175 | 5 | 2 | Marie | 200 | 4 |
4 | Bob | 175 | 5 | 6 | John Cena | 400 | 3 |
4 | Bob | 175 | 5 | 3 | Alex | 350 | 2 |
4 | Bob | 175 | 5 | 4 | Bob | 175 | 5 |
4 | Bob | 175 | 5 | 5 | Alice | 250 | 1 |
1 | Winston | 500 | 6 | 3 | Alex | 350 | 2 |
1 | Winston | 500 | 6 | 6 | John Cena | 400 | 3 |
1 | Winston | 500 | 6 | 4 | Bob | 175 | 5 |
1 | Winston | 500 | 6 | 1 | Winston | 500 | 6 |
1 | Winston | 500 | 6 | 2 | Marie | 200 | 4 |
1 | Winston | 500 | 6 | 5 | Alice | 250 | 1 |
分析
只有turn等于1的人进入公交车 此时总重就为 turn=1
只有turn等于1,2的人进入公交车 此时总重就为 turn=1与turn=2的和
只有turn等于1,2,3 的人进入公交车 此时总重就为 turn=1-3
只有turn等于1,2,3,4的人进入公交车 此时总重就为 turn=1-4
所以只需要对他们按照左turn分组求 右trun的和 即为前n人上车时的总重
- 此时对上层结果处理 分组求和
select *, sum(b.weight) from Queue a , Queue b
where a.turn>=b.turn
group by a.turn
order by a.turn
此时结果
person_id | person_name | weight | turn | person_id | person_name | weight | turn | sum(b.weight) |
---|---|---|---|---|---|---|---|---|
5 | Alice | 250 | 1 | 5 | Alice | 250 | 1 | 250 |
3 | Alex | 350 | 2 | 5 | Alice | 250 | 1 | 600 |
6 | John Cena | 400 | 3 | 5 | Alice | 250 | 1 | 1000 |
2 | Marie | 200 | 4 | 5 | Alice | 250 | 1 | 1200 |
4 | Bob | 175 | 5 | 5 | Alice | 250 | 1 | 1375 |
1 | Winston | 500 | 6 | 5 | Alice | 250 | 1 | 1875 |
所以只需要筛选出小于等于1000的倒序第一即为最后的答案
最终解答
select person_name from(
select a.person_name, sum(b.weight) as weightsum from Queue a , Queue b
where a.turn>=b.turn
group by a.turn
order by a.turn
) as a
where weightsum <=1000
order by weightsum desc
limit 1