在双十一当天,按每 2 小时为一个时间段(0-2, 2-4 ... 22-24),统计每个时间段的 GMV 占当天全天 GMV 的百分比
面试真题:双十一分时段 GMV 占比统计
题目描述:
在双十一当天(2023-11-11),请你编写一个 SQL 统计每个时间段(以每 2 小时为一个时间段,如 0-2, 2-4 ... 22-24)的 GMV(GMV 指支付金额)占当天全天 GMV 的百分比。
示例数据
订单表:user_orders
| order_id (订单ID) | user_id (用户ID) | pay_time (支付时间) | payment_amount (支付金额) |
|---|---|---|---|
| 1001 | 101 | 2023-11-11 00:15:30 | 150.00 |
| 1002 | 102 | 2023-11-11 01:45:00 | 250.00 |
| 1003 | 103 | 2023-11-11 03:10:00 | 100.00 |
| 1004 | 104 | 2023-11-11 08:30:00 | 300.00 |
| 1005 | 105 | 2023-11-11 11:59:00 | 200.00 |
| 1006 | 106 | 2023-11-11 14:15:00 | 400.00 |
| 1007 | 107 | 2023-11-11 17:50:00 | 150.00 |
| 1008 | 108 | 2023-11-11 20:45:00 | 500.00 |
| 1009 | 109 | 2023-11-11 23:10:00 | 350.00 |
| 1010 | 110 | 2023-11-11 23:55:00 | 100.00 |
期望输出结果
| time_interval (时间段) | gmv (当前段GMV) | gmv_percentage (占比%) |
|---|---|---|
| 0-2 | 400.00 | 16.00% |
| 2-4 | 100.00 | 4.00% |
| 8-10 | 300.00 | 12.00% |
| 10-12 | 200.00 | 8.00% |
| 14-16 | 400.00 | 16.00% |
| 16-18 | 150.00 | 6.00% |
| 20-22 | 500.00 | 20.00% |
| 22-24 | 450.00 | 18.00% |
(注:全天总 GMV 为 2500.00。未产生成交的时间段不显示,或通过左连接补 0,本题默认只输出有交易的时间段。)
SQL 标准答案
sql
WITH daily_orders AS (
-- 1. 过滤双十一当天数据,并计算每笔订单属于哪个2小时区间
SELECT
payment_amount,
FLOOR(HOUR(pay_time) / 2) * 2 AS interval_start
FROM user_orders
WHERE DATE(pay_time) = '2023-11-11'
),
interval_gmv AS (
-- 2. 按区间分组,统计各区间的 GMV 总额
SELECT
CONCAT(interval_start, '-', interval_start + 2) AS time_interval,
SUM(payment_amount) AS gmv,
interval_start
FROM daily_orders
GROUP BY interval_start
)
-- 3. 使用窗口函数计算全天总 GMV 并求得占比
SELECT
time_interval,
gmv,
CONCAT(ROUND(gmv / SUM(gmv) OVER () * 100, 2), '%') AS gmv_percentage
FROM interval_gmv
ORDER BY interval_start;
核心考点与解题思路剖析(面试官视角的加分项)
这道题是电商数据分析(SQL 笔试)中非常经典的一道分时段趋势分析题,主要考察候选人的以下几个核心能力:
1. 时间维度的“分桶”处理(Bucket Grouping)
- 难点:如何把连续的
24小时划分为12个2小时的区间(如 0-2, 2-4)。 - 解法:
- 利用
HOUR(pay_time)提取出小时(0-23)。 - 通过向下取整的数学技巧:
FLOOR(HOUR(pay_time) / 2) * 2。- 若小时为
0或1:FLOOR(0/2)*2和FLOOR(1/2)*2结果都是0。 - 若小时为
2或3:结果都是2。
- 若小时为
- 通过
CONCAT(interval_start, '-', interval_start + 2)拼接出漂亮的时间段标签0-2。
- 利用
- 面试加分点:不建议使用冗长的
CASE WHEN HOUR(pay_time) BETWEEN 0 AND 1 THEN '0-2' ...,这种数学分桶方式更体现代码的优雅与高级感。
2. 分母的动态计算(窗口函数的妙用)
- 难点:在对时间段进行
GROUP BY的同时,如何拿到全天不分组的总 GMV。 - 常规解法:写一个子查询
SELECT SUM(payment_amount) FROM user_orders ...,然后进行笛卡尔积关联,这样会多扫描一次表,性能较差。 - 最优解法:使用窗口函数
SUM(gmv) OVER ()。在已经GROUP BY之后的结果集上,不加PARTITION BY地使用SUM()窗口函数,能直接计算出所有行的gmv总和,作为占比的分母。
3. 细节处理与格式化
- 百分比转换:使用
ROUND(ratio * 100, 2)保留两位小数,再用CONCAT(..., '%')转换为易读的百分比格式。 - 排序问题:时间段如果是字符串(如 "10-12" 比 "2-4" 的字符顺序更靠前),直接用
ORDER BY time_interval会导致排序混乱。本答案在 CTE 中保留了数值型的interval_start,并在最后用它进行ORDER BY,确保了输出结果按时间流严格正序。
右滑查看面试常问