计算用户领取优惠券到实际消费使用该券的转化率,并统计使用优惠券的平均时间间隔(领券后多少小时内使用)
面试题:优惠券领用双维度分析(转化率与核销时效)
在电商、O2O等业务场景中,优惠券是促进用户转化的重要手段。本题旨在考察候选人对漏斗转化分析以及时间跨度计算(时间差)的 SQL 编写能力及业务理解。
1. 场景数据准备
假设系统中有两张表:领券记录表 (coupon_receive) 和 使用/消费记录表 (coupon_use)。
表1:领券记录表 (coupon_receive)
| user_id (用户ID) | coupon_id (优惠券ID) | receive_time (领券时间) |
|---|---|---|
| 1001 | C_01 | 2023-10-01 10:00:00 |
| 1002 | C_01 | 2023-10-01 12:30:00 |
| 1003 | C_02 | 2023-10-02 09:15:00 |
| 1004 | C_01 | 2023-10-02 14:00:00 |
| 1005 | C_03 | 2023-10-03 18:00:00 |
| 1001 | C_02 | 2023-10-04 08:00:00 |
表2:用券/消费记录表 (coupon_use)
| user_id (用户ID) | coupon_id (优惠券ID) | order_id (订单ID) | use_time (使用时间) |
|---|---|---|---|
| 1001 | C_01 | ORD_9901 | 2023-10-01 10:45:00 |
| 1002 | C_01 | ORD_9902 | 2023-10-01 22:30:00 |
| 1005 | C_03 | ORD_9903 | 2023-10-05 18:00:00 |
| 1001 | C_02 | ORD_9904 | 2023-10-04 08:15:00 |
注:用户 1003 领券后未消费;用户 1004 领券后未消费。
2. 面试题目要求
请编写一条 SQL 语句,计算出以下两个指标:
- 券转化率 (Conversion Rate):实际使用优惠券的记录数占总领券记录数的比例(结果以百分比形式展现,保留2位小数)。
- 平均使用时间间隔 (Average Interval Hours):从领券到用券的平均时间间隔,单位为小时(保留2位小数)。
提示:使用时间必须晚于或等于领券时间才算作有效转化。
3. 标准 SQL 解答 (基于 MySQL 8.0+)
sql
SELECT
-- 1. 计算总领券量
COUNT(r.user_id) AS total_received,
-- 2. 计算实际使用量
COUNT(u.user_id) AS total_used,
-- 3. 计算转化率 (用券量 / 领券量)
ROUND(
COUNT(u.user_id) * 100.0 / COUNT(r.user_id),
2
) AS conversion_rate_pct,
-- 4. 计算平均用券时间间隔(小时)
ROUND(
AVG(TIMESTAMPDIFF(SECOND, r.receive_time, u.use_time) / 3600.0),
2
) AS avg_use_interval_hours
FROM
coupon_receive r
LEFT JOIN
coupon_use u
ON
r.user_id = u.user_id
AND r.coupon_id = u.coupon_id
AND u.use_time >= r.receive_time;
预期输出结果:
| total_received | total_used | conversion_rate_pct | avg_use_interval_hours |
|---|---|---|---|
| 6 | 4 | 66.67 | 15.19 |
(解析提示:有效用券记录共4条。时间差分别为:1001-C_01:0.75小时;1002-C_01:10.0小时;1005-C_03:48.0小时;1001-C_02:0.25小时。平均值:(0.75 + 10.0 + 48.0 + 0.25) / 4 = 14.75小时。若用秒换算更精确:(2700 + 36000 + 172800 + 900) / 4 / 3600 = 14.75小时)
4. 核心考点与面试官心理解析
考点一:LEFT JOIN 的合理运用(漏斗分析基础)
- 理解意图:计算转化率必须保留分母(所有领券的用户),所以必须以领券表
coupon_receive为主表进行LEFT JOIN。如果错误地使用了INNER JOIN,未用券的数据(1003、1004)就会被过滤掉,导致转化率计算恒为 100%。 - 加分项:在
ON条件中直接限制u.use_time >= r.receive_time,防止“先消费后补券”的异常脏数据干扰统计。
考点二:聚合函数 COUNT 的特征
- 理解意图:
COUNT(r.user_id)会统计所有领券记录,而COUNT(u.user_id)仅统计LEFT JOIN成功匹配到的非NULL记录。这能直接在一行 SQL 中优雅地解决分子和分母的计算,避免写复杂的子查询。
考点三:高精度时间差计算 (TIMESTAMPDIFF / DATEDIFF)
- 避坑指南:很多候选人会直接使用
DATEDIFF(u.use_time, r.receive_time),但DATEDIFF仅计算天数差,无法精确到小时。 - 正确做法:使用
TIMESTAMPDIFF(SECOND, ...)计算秒数差,再除以3600.0转换为小时。注意除数要带小数点(如 3600.0),在某些数据库(如 SQL Server/PostgreSQL)中,整除会丢失小数精度。
考点四:业务思维扩展(面试追问准备)
如果候选人完美答出上述 SQL,面试官通常会进行以下追问以考察其业务Sense:
- 追问一:如果同一个用户多次领取同一张券,又多次使用,你的 Join 会不会产生笛卡尔积?
- 应对策略:在实际业务中,券实例通常有唯一的
get_id(领券实例ID)。若无此ID,需在 JOIN 时加上时间顺序逻辑(如:用券时间必须在领券时间之后,且是距离最近的一次领券)。
- 应对策略:在实际业务中,券实例通常有唯一的
- 追问二:如何按优惠券维度 (coupon_id) 统计这些指标?
- 应对策略:在 SQL 结尾加上
GROUP BY r.coupon_id,并在SELECT中加入r.coupon_id即可。
- 应对策略:在 SQL 结尾加上