统计每日的销售总额中,新用户(当天注册且当天消费)与老用户(非当天注册但当天有消费)各自的金额占比
面试题:每日新老用户销售额占比统计
1. 题目背景
在电商、零售等业务场景中,分析每日销售额的构成是极其重要的运营指标。通过监控“新用户”(当天注册且当天消费)与“老用户”(非当天注册但当天有消费)的销售额占比,可以评估获客活动的效果以及老用户的留存与复购活力。
请编写 SQL 统计每日的销售总额,以及新用户、老用户各自的金额和占比(占比保留两位小数,并展示为百分数形式,如 50.00%)。
2. 示例数据
表1:用户注册表 users
| user_id (用户ID) | register_date (注册日期) |
|---|---|
| U1 | 2023-10-01 |
| U2 | 2023-10-01 |
| U3 | 2023-10-02 |
| U4 | 2023-10-02 |
| U5 | 2023-09-30 |
表2:订单销售表 orders
| order_id (订单ID) | user_id (用户ID) | order_date (订单日期) | amount (订单金额) |
|---|---|---|---|
| O001 | U1 | 2023-10-01 | 100.00 |
| O002 | U2 | 2023-10-01 | 150.00 |
| O003 | U5 | 2023-10-01 | 250.00 |
| O004 | U1 | 2023-10-02 | 200.00 |
| O005 | U3 | 2023-10-02 | 300.00 |
| O006 | U4 | 2023-10-02 | 100.00 |
| O007 | U2 | 2023-10-03 | 100.00 |
| O008 | U3 | 2023-10-03 | 100.00 |
3. 期望输出结果
| order_date | total_amount | new_user_amount | old_user_amount | new_ratio | old_ratio |
|---|---|---|---|---|---|
| 2023-10-01 | 500.00 | 250.00 | 250.00 | 50.00% | 50.00% |
| 2023-10-02 | 600.00 | 400.00 | 200.00 | 66.67% | 33.33% |
| 2023-10-03 | 200.00 | 0.00 | 200.00 | 0.00% | 100.00% |
4. SQL 编写
sql
SELECT
o.order_date,
-- 1. 计算每日总销售额
SUM(o.amount) AS total_amount,
-- 2. 分别计算新老用户销售额
SUM(CASE WHEN o.order_date = u.register_date THEN o.amount ELSE 0 END) AS new_user_amount,
SUM(CASE WHEN o.order_date > u.register_date THEN o.amount ELSE 0 END) AS old_user_amount,
-- 3. 计算新老用户占比,转化为百分比格式
CONCAT(
ROUND(
SUM(CASE WHEN o.order_date = u.register_date THEN o.amount ELSE 0 END) / SUM(o.amount) * 100,
2
),
'%'
) AS new_ratio,
CONCAT(
ROUND(
SUM(CASE WHEN o.order_date > u.register_date THEN o.amount ELSE 0 END) / SUM(o.amount) * 100,
2
),
'%'
) AS old_ratio
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
GROUP BY o.order_date
ORDER BY o.order_date;
5. 核心考点与 SQL 分析
考点一:多表关联(Join)与维度对齐
- 分析:要判断用户是否是“当天注册且当天消费”,需要将销售数据和用户注册数据结合。这里以订单表
orders为主表,通过user_id关联users表,从而获取每个下单用户的注册日期(register_date)。 - 面试提分点:使用
LEFT JOIN而不是INNER JOIN可以防止部分缺失注册信息的异常用户导致订单数据丢失,在实际数仓中更加严谨。
考点二:新老用户口径的逻辑定义(Conditional Aggregation)
- 新用户条件:订单日期等于注册日期(
o.order_date = u.register_date)。 - 老用户条件:订单日期大于注册日期(
o.order_date > u.register_date)。 - 分析:使用
CASE WHEN表达式实现条件聚合。当满足对应条件时返回订单金额,否则返回0。这种方式可以在一次分组(GROUP BY)中同时计算出新老用户的销售额,避免了多次子查询或UNION,执行效率极高。
考点三:数值计算、精度保留与格式化
- 除法防报错:实际业务中需注意分母为0的情况(虽然每日总额为0通常没有订单数据,但在更复杂的聚合中常用
NULLIF(SUM(o.amount), 0)来防止报错)。 - 格式化函数:
ROUND(..., 2):用于保留 2 位小数。CONCAT(..., '%'):用于拼接百分号。在 Hive 或 MySQL 中,此操作会将数值转换为字符串并展示为易读的指标。