Apple 的 SQL 面试真题。
Original Question
Given a Trip table, find each driver's consecutive 8-hour period with the highest total trip earnings.
A window starts at any trip's start_time and includes that driver's trips starting within the following 8 hours.
Rank drivers by their maximum 8-hour earnings and calculate a 10% bonus.
给定一张 Trip 行程表,找出每位司机总收入最高的连续 8 小时时间段。
每个时间窗口从某次行程的开始时间算起,统计该司机随后 8 小时内所有开始的行程收入。
根据每位司机最高的 8 小时收入进行排名,并计算 10% 的奖金。
假设 Trip 表包含以下字段:
| 字段 | 含义 |
|---|---|
| driver_id | 司机 ID |
| trip_id | 行程 ID |
| start_time | 行程开始时间 |
| earnings | 行程收入 |
最终需要返回每位司机收入最高的 8 小时窗口、该窗口的总收入、司机排名和对应奖金。
这道题最容易犯的错误,是直接按照固定的 8 小时时间段进行分组。
例如,把一天划分成 00:00–08:00、08:00–16:00 和 16:00–24:00,再分别计算收入。
但题目明确规定,窗口可以从任何一次行程的开始时间出发。这意味着窗口可能是 09:15–17:15,也可能是 22:30–次日 06:30,不能按照固定时段划分。
真正需要解决的问题,是如何枚举所有可能的 8 小时窗口,并找出收入最高的那个。
好在题目已经给出了一个重要限制:窗口必须从某次行程的 start_time 开始。
因此,不需要枚举一天中每一秒的时间,只需要把司机的每个不同的行程开始时间当作一个候选窗口起点。
假设某位司机有以下行程:
| 开始时间 | 收入 |
|---|---|
| 09:00 | $40 |
| 12:00 | $80 |
| 16:30 | $50 |
| 20:10 | $100 |
如果从 09:00 开始,统计 09:00–17:00 内开始的行程,总收入为 $170。
如果从 12:00 开始,统计 12:00–20:00 内开始的行程,总收入为 $130。20:10 开始的行程已经超出窗口,因此不能算进去。
同样,从 16:30 开始,窗口内的收入为 $150。
比较这些候选窗口后,就能找到该司机的最高 8 小时收入:$170,窗口从 09:00 开始。
对每位司机重复这一过程,再按照最高收入排序即可。
SQL 实现
这道题可以使用 Self Join(自连接)配合窗口函数完成。
随后,使用 SUM(earnings) 计算每个候选窗口的总收入。
ROW_NUMBER() 负责选出每位司机收入最高的窗口。如果有多个窗口收入相同,则优先选择开始时间最早的窗口,保证结果唯一且稳定。
最后,使用 DENSE_RANK() 根据各司机的最高 8 小时收入进行全局排名,并计算 10% 的奖金。
这里需要区分两个排名操作:前一个排名是在每位司机内部选择最佳窗口,后一个排名才是在所有司机之间比较收入。两者的分组范围不同。
面试官可能进一步追问什么?
如果只是写出SQL,基本已经完成了题目的功能要求。但面试官还和候选人继续讨论几个问题。
首先是时间窗口的边界。
题目统计的是在 8 小时内开始的行程,而不是在这段时间内完成的行程。因此,即使某次行程在窗口结束以后才完成,只要它的 start_time 落在窗口之内,收入就会计入。
其次是跨天问题。
窗口可能从晚上开始,延续到第二天凌晨。因此不能简单使用 DATE(start_time) 把行程限制在同一天。直接比较完整的时间戳才能正确处理跨日窗口。
另一个问题是性能。
自连接的写法比较直观,但如果某位司机有大量行程,就可能产生较多的匹配记录。对于大规模数据,可以考虑先按司机和开始时间排序,再使用滑动窗口或双指针计算每个候选起点的收入。
在这种优化方案下,每位司机的行程经过排序后,可以用两个指针维护当前 8 小时区间,同时维护收入总和。当窗口向前移动时,移除已经超出范围的
对于准备 Apple、Google、Meta 等公司技术面试的同学,也可以通过模拟面试提前熟悉这类数据分析题的解题节奏和沟通方式。
我们也有代面试,面试辅助,OA代写等服务助您早日上岸~

