对比计算每个快递柜在工作日(周一至周五)与周末(周六、周日)的用户平均取件耗时(分钟数)
SparkSQL 面试题:快递柜工作日与周末用户平均取件耗时对比
1. 题目背景
在智能快递柜运营中,分析用户在工作日(周一至周五)与周末(周六、周日)的取件行为差异,能够帮助运营团队合理规划投递时间、调整免费暂存时长以及优化逆向物流。本题要求计算并对比每个快递柜在工作日与周末的平均取件耗时(单位:分钟)。
2. 示例数据
表 1:快递柜投递取件记录表 (t_cabinet_record)
该表记录了每一快件的投递时间和用户实际取件时间。
记录ID (record_id) |
快递柜ID (cabinet_id) |
投递时间 (dropoff_time) |
取件时间 (pickup_time) |
|---|---|---|---|
| R001 | CAB001 | 2023-10-23 08:30:00 (周一) | 2023-10-23 18:30:00 (周一) |
| R002 | CAB001 | 2023-10-24 09:00:00 (周二) | 2023-10-24 21:00:00 (周二) |
| R003 | CAB001 | 2023-10-28 10:00:00 (周六) | 2023-10-28 11:30:00 (周六) |
| R004 | CAB001 | 2023-10-29 09:00:00 (周日) | 2023-10-29 11:00:00 (周日) |
| R005 | CAB002 | 2023-10-23 10:00:00 (周一) | 2023-10-24 10:00:00 (周二) |
| R006 | CAB002 | 2023-10-28 08:00:00 (周六) | 2023-10-29 08:00:00 (周日) |
3. 期望输出结果
快递柜ID (cabinet_id) |
工作日平均取件耗时_分钟 (weekday_avg_minutes) |
周末平均取件耗时_分钟 (weekend_avg_minutes) |
耗时差异_分钟 (diff_minutes) |
|---|---|---|---|
| CAB001 | 660.00 | 105.00 | 555.00 |
| CAB002 | 1440.00 | 1440.00 | 0.00 |
数据解析说明:
- CAB001:
- 工作日取件:R001耗时600分钟,R002耗时720分钟,平均 (600+720)/2 = 660分钟。
- 周末取件:R003耗时90分钟,R004耗时120分钟,平均 (90+120)/2 = 105分钟。
- CAB002:
- 工作日取件:R005耗时1440分钟(跨天,但取件日24号是周二,仍算工作日),平均1440分钟。
- 周末取件:R006耗时1440分钟(跨天,取件日29号是周日,算周末),平均1440分钟。
4. SparkSQL 核心解法答案
sql
WITH time_extracted AS (
SELECT
cabinet_id,
-- 计算取件耗时(秒数),除以 60 得到分钟数
(CAST(pickup_time AS LONG) - CAST(dropoff_time AS LONG)) / 60.0 AS duration_minutes,
-- 提取取件时间是星期几:1 代表周日,2 代表周一,...,7 代表周六
dayofweek(pickup_time) as pickup_day_of_week
FROM t_cabinet_record
WHERE pickup_time IS NOT NULL AND dropoff_time IS NOT NULL
),
classified_data AS (
SELECT
cabinet_id,
duration_minutes,
-- 1(周日) 和 7(周六) 为周末,其余为工作日
CASE
WHEN pickup_day_of_week IN (1, 7) THEN 'weekend'
ELSE 'weekday'
END AS day_type
FROM time_extracted
)
SELECT
cabinet_id,
-- 计算工作日平均耗时
ROUND(AVG(CASE WHEN day_type = 'weekday' THEN duration_minutes END), 2) AS weekday_avg_minutes,
-- 计算周末平均耗时
ROUND(AVG(CASE WHEN day_type = 'weekend' THEN duration_minutes END), 2) AS weekend_avg_minutes,
-- 计算工作日与周末的差值
ROUND(
COALESCE(AVG(CASE WHEN day_type = 'weekday' THEN duration_minutes END), 0) -
COALESCE(AVG(CASE WHEN day_type = 'weekend' THEN duration_minutes END), 0), 2
) AS diff_minutes
FROM classified_data
GROUP BY cabinet_id;
5. SparkSQL 深度分析与面试应对指南
在面试中,仅仅写出上述 SQL 是不够的。面试官往往会针对你的答案进行追问,以考察你的 Spark 底层机制、时间函数坑点及大数据优化思维。以下是针对该题目的核心考点深度解析:
考点一:时间与日期函数的“坑”
dayofweek函数的返回值标准:- 在 SparkSQL 中,默认的
dayofweek函数遵循 标准库(Sunday = 1, Saturday = 7)。 - 面试加分点:主动提及
date_format(pickup_time, 'u')也可以用来获取星期几(1 代表周一,7 代表周日)。在多引擎(如 Hive, Presto, Spark)混合环境中,确保时间函数的行为一致性非常重要。
- 在 SparkSQL 中,默认的
- 时间差计算:
- 直接将
TIMESTAMP转换为LONG类型进行相减,得到的是秒数差值。这种做法比使用unix_timestamp(t1) - unix_timestamp(t2)性能更好,因为减少了函数调用开销。
- 直接将
考点二:行转列(Pivot)的优雅实现
本题要求对比“工作日”与“周末”两个维度,属于典型的条件聚合(Conditional Aggregation)。
- 为什么用
CASE WHEN配合AVG:
在 Spark SQL 中,SELECT AVG(CASE WHEN ... END)比先GROUP BY cabinet_id, day_type再进行JOIN或PIVOT操作更高效。因为它只需要一次 Shuffle(按cabinet_id分组),而通过多表关联来实现行转列会引入多次 Shuffle 阶段,严重降低性能。
考点三:Spark 性能调优与边界处理
- 数据倾斜(Data Skew)风险:
- 痛点:某些“网红小区”或“核心写字楼”的快递柜(如
CAB001)业务量极大,而部分偏远地区的快递柜数据极少。按cabinet_id进行GROUP BY时,会导致严重的数据倾斜。 - 解决方案:
- 如果确定存在倾斜,可以采用两阶段聚合(加盐局部聚合 + 去盐全局聚合)。
- 开启 Spark 3.x 的 AQE(Adaptive Query Execution,自适应查询执行),Spark 会自动合并小分区、处理倾斜的分区。
- 痛点:某些“网红小区”或“核心写字楼”的快递柜(如
- Null 值防范:
- 用户可能存在“未取件”的情况(即
pickup_time为空)。在第一步WHERE过滤中必须显式排除,否则会导致NullPointerException或计算结果不准。 - 计算差值时,使用
COALESCE(..., 0)防止某些快递柜在周末或工作日没有数据时,出现NULL - 100 = NULL的逻辑错误。
- 用户可能存在“未取件”的情况(即