基于本文回答

播面 播面

文解图解播客视频,多维讲透八股文
0
评论

分析每个快递员投递快件的习惯,统计其投递的包裹在大、中、小三种规格格口中的占比分布

SparkSQL 面试题:快递员投递包裹规格占比分析

1. 题目背景与要求

在物流行业中,分析快递员的投递习惯对于优化快递柜的格口配置(大、中、小格口的比例)至关重要。
请编写一段 SparkSQL 逻辑,计算每个快递员投递的包裹在大、中、小三种规格格口中的占比分布(结果保留4位小数,并以百分比形式或小数形式呈现)。


2. 示例数据

表1:包裹投递明细表 (t_delivery_record)

记录了每次投递的流水信息。

投递ID (delivery_id) 快递员ID (courier_id) 包裹编号 (package_id) 投递格口规格 (box_size) 投递时间 (delivery_time)
d001 C001 P1001 大 (Large) 2023-10-01 08:00:00
d002 C001 P1002 中 (Medium) 2023-10-01 08:30:00
d003 C001 P1003 中 (Medium) 2023-10-01 09:00:00
d004 C001 P1004 小 (Small) 2023-10-01 09:15:00
d005 C002 P1005 大 (Large) 2023-10-01 10:00:00
d006 C002 P1006 大 (Large) 2023-10-01 10:30:00
d007 C002 P1007 小 (Small) 2023-10-01 11:00:00
d008 C003 P1008 中 (Medium) 2023-10-01 11:15:00
期望输出结果
快递员ID (courier_id) 总投递量 (total_count) 大格口占比 (large_ratio) 中格口占比 (medium_ratio) 小格口占比 (small_ratio)
C001 4 0.2500 (25.00%) 0.5000 (50.00%) 0.2500 (25.00%)
C002 3 0.6667 (66.67%) 0.0000 (0.00%) 0.3333 (33.33%)
C003 1 0.0000 (0.00%) 1.0000 (100.00%) 0.0000 (0.00%)

3. SparkSQL 核心解法

sql
SELECT 
    courier_id,
    COUNT(1) AS total_count,
    -- 使用 CASE WHEN 配合 SUM 进行行转列与条件计数
    ROUND(SUM(CASE WHEN box_size = '大 (Large)' THEN 1 ELSE 0 END) / COUNT(1), 4) AS large_ratio,
    ROUND(SUM(CASE WHEN box_size = '中 (Medium)' THEN 1 ELSE 0 END) / COUNT(1), 4) AS medium_ratio,
    ROUND(SUM(CASE WHEN box_size = '小 (Small)' THEN 1 ELSE 0 END) / COUNT(1), 4) AS small_ratio
FROM 
    t_delivery_record
GROUP BY 
    courier_id;

4. SparkSQL 深度剖析与面试加分项

① 核心技术点:行转列(Pivot)的经典实现

本题是典型的“行转列”应用场景。在分布式计算中,频繁使用 JOIN 会导致多次 Shuffle,严重影响性能。

  • 推荐做法:使用 GROUP BY + CASE WHEN + SUM。这种方式只需要对数据进行一次全表扫描和一次 Shuffle(Group By),即可同时计算出总数以及各个维度的子项数。
  • Spark 专属优化:在 SparkSQL 中,也可以使用原生 PIVOT 语法,但手写 CASE WHEN 具有更好的跨引擎兼容性(Hive/Presto/FlinkSQL 通用)。
② 避免“除以零(Division by Zero)”的健壮性设计

在实际生产环境中,如果 total_count 为 0(虽然在此业务场景下由于 GROUP BY 保证了至少为1,但如果是关联外部表则可能为0),直接相除会导致 NaN 或报错。

  • 面试官加分点:展示你对数据脏值的敏感度。可以使用 NULLIF(COUNT(1), 0) 来规避除零异常:
    sql
    ROUND(SUM(CASE WHEN box_size = '大 (Large)' THEN 1 ELSE 0 END) / NULLIF(COUNT(1), 0), 4)
③ Spark 执行计划与性能调优分析
  • 数据倾斜(Data Skew):如果某些“头部快递员”每天投递几万件,而普通快递员只投递几十件,GROUP BY courier_id 会导致严重的数据倾斜
    • 解决方案
      1. 双重聚合(两阶段聚合):先对 courier_id 加随机前缀进行局部聚合,再去掉前缀进行全局聚合。
      2. 启用 Spark 3.x Adaptive Query Execution (AQE):设置 spark.sql.adaptive.skewJoin.enabled = true,Spark 会自动检测倾斜的分区并进行拆分。