给定整点秒杀活动商品表(商品ID、秒杀开始时间)和订单表,统计秒杀开始后前 5 分钟内对应商品的售出数量和交易额。
面试题:整点秒杀活动前 5 分钟销售统计
1. 背景描述
在电商系统的秒杀活动中,运营团队需要实时监控秒杀商品的销售表现。请编写一个 SQL,统计每个秒杀商品在秒杀开始后前 5 分钟内(含开始时刻,至第 5 分钟整,即 [start_time, start_time + 5 minutes])的累计售出数量和累计交易额。如果某商品在规定时间内没有售出,售出数量和交易额显示为 0。
2. 示例数据
表 1:秒杀活动商品表 (flash_sale_products)
| product_id (商品ID) | start_time (秒杀开始时间) |
|---|---|
| 101 | 2023-11-11 10:00:00 |
| 102 | 2023-11-11 14:00:00 |
| 103 | 2023-11-11 20:00:00 |
表 2:订单表 (orders)
| order_id (订单ID) | product_id (商品ID) | order_time (下单时间) | quantity (购买数量) | price (单价) |
|---|---|---|---|---|
| 1 | 101 | 2023-11-11 10:01:30 | 2 | 50.00 |
| 2 | 101 | 2023-11-11 10:04:59 | 1 | 50.00 |
| 3 | 101 | 2023-11-11 10:06:00 | 5 | 50.00 |
| 4 | 102 | 2023-11-11 13:59:50 | 1 | 100.00 |
| 5 | 102 | 2023-11-11 14:02:15 | 3 | 100.00 |
| 6 | 103 | 2023-11-11 20:05:01 | 2 | 200.00 |
| 7 | 103 | 2023-11-11 20:00:00 | 1 | 200.00 |
3. 期望输出结果
| product_id | total_quantity | total_amount |
|---|---|---|
| 101 | 3 | 150.00 |
| 102 | 3 | 300.00 |
| 103 | 1 | 200.00 |
样例解析:
- 商品 101:秒杀 10:00 开始。订单 1 和 订单 2 在前 5 分钟内(10:05:00之前),计入统计。订单 3 在 10:06 超过 5 分钟,不计入。总数量 = 2 + 1 = 3,总金额 = 150.00。
- 商品 102:秒杀 14:00 开始。订单 4 在秒杀前下单,不计入;订单 5 在 14:02:15 计入。总数量 = 3,总金额 = 300.00。
- 商品 103:秒杀 20:00 开始。订单 7 正好在开始时刻,计入;订单 6 在 20:05:01 超过了 5 分钟限制(截止 20:05:00),不计入。总数量 = 1,总金额 = 200.00。
4. 标准 SQL 解答 (以 MySQL 为例)
sql
SELECT
p.product_id,
COALESCE(SUM(o.quantity), 0) AS total_quantity,
COALESCE(SUM(o.quantity * o.price), 0.00) AS total_amount
FROM
flash_sale_products p
LEFT JOIN
orders o ON p.product_id = o.product_id
AND o.order_time >= p.start_time
AND o.order_time <= DATE_ADD(p.start_time, INTERVAL 5 MINUTE)
GROUP BY
p.product_id;
5. 核心难点与 SQL 分析
在面试中,这道题看似简单,但有几个关键的隐藏考点,候选人需要特别注意:
考点一:关联条件与过滤时机(LEFT JOIN 的正确姿势)
- 错误写法:使用
INNER JOIN然后在WHERE子句中过滤时间。- 原因:如果某个秒杀商品在前 5 分钟内没有产生任何销售,
INNER JOIN会把该商品过滤掉。要求中明确指出“如果没有售出,显示为 0”,因此必须使用LEFT JOIN。
- 原因:如果某个秒杀商品在前 5 分钟内没有产生任何销售,
- 错误写法:在
LEFT JOIN后使用WHERE o.order_time <= ...过滤。- 原因:
WHERE过滤发生在JOIN之后,会把未匹配到订单(即o.order_time为NULL)的商品行过滤掉,使其退化为INNER JOIN的效果。时间过滤条件必须写在ON子句中。
- 原因:
考点二:时间跨度计算 (DATE_ADD)
- 题目要求“前 5 分钟内”。在 SQL 中,通常使用
DATE_ADD(p.start_time, INTERVAL 5 MINUTE)或直接使用时间戳相减:o.order_time BETWEEN p.start_time AND p.start_time + INTERVAL 5 MINUTE。 - 注意边界值处理(例如:秒杀开始是
10:00:00,截止是10:05:00)。使用>=和<=可以精确覆盖这 5 分钟的时间滑窗。
考点三:空值处理 (COALESCE / IFNULL)
- 对于通过
LEFT JOIN产生的未匹配行,其o.quantity和o.price为NULL。 - 使用
SUM(o.quantity)得到的结果会是NULL,不符合题目“显示为 0”的要求。因此,必须使用COALESCE(SUM(...), 0)来兜底。
6. 面试官追问/优化方向(加分项)
大表关联的性能优化
- 追问:如果
orders表有几亿条数据,这个查询会很慢,如何优化? - 答案:
- 索引设计:在
orders表上建立复合索引(product_id, order_time),这样可以通过联合索引快速定位到特定商品的下单时间区间。 - 先过滤再关联:如果秒杀商品很少,而订单表极大,可以利用分布式计算引擎(如 Hive/Spark)先对
orders按product_id进行合理分区(Bucket),或者先限定orders表的时间范围(例如只取秒杀当天的订单),减小参与 JOIN 的数据量。
- 索引设计:在
- 追问:如果
高并发下的分布式事务/时区问题
- 追问:如果服务器分布在不同时区,如何确保时间比对准确?
- 答案:在数据库中统一存储 UTC 时间(Timestamp 类型),在 SQL 运算时利用时区函数统一转换后再进行加减比较。
右滑查看面试常问