首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >阿里巴巴大数据面试SQL-首次到二次购买间隔

阿里巴巴大数据面试SQL-首次到二次购买间隔

作者头像
数据仓库晨曦
发布2026-07-11 10:09:15
发布2026-07-11 10:09:15
1300
举报
文章被收录于专栏:数据仓库技术数据仓库技术

一、题目背景

这道题来自阿里巴巴淘宝事业部的用户增长数据分析岗面试。首单到第二单的间隔是衡量"用户激活速度"的关键指标——如果用户买了一次就再也不来了,说明产品体验或商品吸引力不足。反之,间隔越短说明用户越容易"上瘾"。

业务场景:运营团队在做新用户首单后的push推送时机决策时,需要知道"大多数用户隔多久下第二单",然后在那个时间窗口内推送优惠券。这道题计算的就是那个时间窗口的基准值。

二、题目

现有一张用户订单表 t10_user_orders,计算用户从首次购买到第二次购买的平均间隔天数。

注意:只有下单>=2次的用户才有"前两单间隔",只下单1次的用户不参与计算。

用户订单表 t10_user_orders:

代码语言:javascript
复制
+-----------+----------+----------------------+
| order_id  | user_id  |      order_time      |
+-----------+----------+----------------------+
| O001      | u01      | 2023-03-01 10:00:00  |
| O002      | u01      | 2023-03-05 10:00:00  |
| O003      | u01      | 2023-03-10 10:00:00  |
| O004      | u02      | 2023-03-02 12:00:00  |
| O005      | u02      | 2023-03-08 12:00:00  |
+-----------+----------+----------------------+

三、思路分析

核心是锚定每个用户的前两笔订单

  1. 行号标记ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time) 为每笔订单编号
  2. 提取前两笔WHERE rn <= 2,同时用 CASE WHEN 把第1笔和第2笔的时间分别放到不同列
  3. 计算间隔DATEDIFF(第2笔日期, 第1笔日期) 得到天数
  4. 排除单次用户:HAVING 过滤掉只买了一次的用户

维度

评分

题目难度

⭐️⭐️⭐️

题目清晰度

⭐️⭐️⭐️⭐️

业务常见度

⭐️⭐️⭐️⭐️

四、逐步推导

步骤1:用 ROW_NUMBER 为每个用户的订单按时间编号

执行SQL

代码语言:javascript
复制
select user_id, order_time,
       row_number() over (partition by user_id order by order_time) as rn
from t10_user_orders

执行结果

代码语言:javascript
复制
+----------+----------------------+-----+
| user_id  |      order_time      | rn  |
+----------+----------------------+-----+
| u01      | 2023-03-01 10:00:00  | 1   |
| u01      | 2023-03-05 10:00:00  | 2   |
| u01      | 2023-03-10 10:00:00  | 3   |
| u02      | 2023-03-02 12:00:00  | 1   |
| u02      | 2023-03-08 12:00:00  | 2   |
+----------+----------------------+-----+
5 rows selected (0.399 seconds)(https://www.dwsql.com)

u01 的3笔订单分别编号为 1、2、3;u02 的2笔订单编号为 1、2。

步骤2:将第1笔和第2笔时间提取到同一行

用 CASE WHEN 将 rn=1 和 rn=2 的时间分别放入 first_order 和 second_order 列,外层 GROUP BY user_id 用 MAX 聚合(因为每列只有一个非NULL值)。

执行SQL

代码语言:javascript
复制
select user_id,
       datediff(max(second_order), max(first_order)) as interval_days
from (
    select user_id,
           case when rn = 1 then order_time end as first_order,
           case when rn = 2 then order_time end as second_order
    from (
        select user_id, order_time,
               row_number() over (partition by user_id order by order_time) as rn
        from t10_user_orders
    ) t1
    where rn <= 2
) t2
group by user_id having interval_days is not null

执行结果

代码语言:javascript
复制
+----------+----------------+
| user_id  | interval_days  |
+----------+----------------+
| u01      | 4              |
| u02      | 6              |
+----------+----------------+
2 rows selected (0.37 seconds)(https://www.dwsql.com)

步骤3:计算平均间隔

u01 从 03-01 首次购买到 03-05 第二次购买间隔 4 天。u02 从 03-02 到 03-08 间隔 6 天。两位用户的平均复购间隔 = (4+6)/2 = 5 天。

执行SQL

代码语言:javascript
复制
select avg(interval_days) as avg_interval_days
from (
select user_id,
       datediff(max(second_order), max(first_order)) as interval_days
from (
    select user_id,
           case when rn = 1 then order_time end as first_order,
           case when rn = 2 then order_time end as second_order
    from (
        select user_id, order_time,
               row_number() over (partition by user_id order by order_time) as rn
        from t10_user_orders
    ) t1
    where rn <= 2
) t2
group by user_id having interval_days is not null
) t3

执行结果

代码语言:javascript
复制
+--------------------+
| avg_interval_days  |
+--------------------+
| 5.0                |
+--------------------+
1 row selected (0.458 seconds)(https://www.dwsql.com)

五、常见坑点

坑1:CASE WHEN + MAX 的技巧理解

有人会疑惑为什么用 MAX() 聚合——因为同一用户的前两笔数据分布在两行,CASE WHEN 让每行只有一个非NULL的日期列,MAX() 在此处的实际作用是"取非NULL值"(因为 NULL < 任何日期值)。

坑2:LEAD 方案需要额外处理

另一种解法是 LEAD(order_time, 1) OVER (PARTITION BY user_id ORDER BY order_time) 获取下一单时间,但 LEAD 让每笔订单都能看到下一笔的时间,你需要额外取 rn=1 那一行才是"首单的下一单"。ROW_NUMBER + CASE WHEN 方案更直观。

坑3:只买了一次的用户

如果用户只下了一单,rn=2 不存在,CASE WHEN 返回 NULL,最终 DATEDIFF(NULL, *) = NULL。需要用 HAVING interval_days IS NOT NULL 或最外层过滤掉这些用户。

六、举一反三

  1. LEAD 替代方案LEAD(order_time) OVER (PARTITION BY user_id ORDER BY order_time) 直接获取下一单时间,配合 WHERE rn=1 取首单的下一单间隔
  2. 扩展到N单间隔:把 rn &lt;= 2 改成 rn &lt;= N,计算"首次购买到第N次购买"的累计间隔和平均每单间隔
  3. 按渠道对比:GROUP BY 加入 channel 字段,对比不同渠道来源的用户首单到二单间隔是否有显著差异
  4. 间隔分布分析:将 interval_days 分段(0-1天/2-3天/4-7天/8-14天/15+天),用 CASE WHEN 分类统计各区间用户数占比

七、知识点总结

考点

说明

ROW_NUMBER + WHERE rn <= 2

精确取每个用户的前两笔订单

CASE WHEN + MAX 列转行

将两行的日期合并到同一行,便于计算差值

DATEDIFF

计算两个日期的天数差

HAVING IS NOT NULL

过滤只买了一单的用户

八、建表语句和数据插入

代码语言:javascript
复制
CREATE TABLE t10_user_orders (
    order_id   string COMMENT '订单ID',
    user_id    string COMMENT '用户ID',
    order_time string COMMENT '下单时间'
) COMMENT '用户订单表';

INSERT INTO t10_user_orders VALUES
('O001','u01','2023-03-01 10:00:00'),('O002','u01','2023-03-05 10:00:00'),
('O003','u01','2023-03-10 10:00:00'),('O004','u02','2023-03-02 12:00:00'),
('O005','u02','2023-03-08 12:00:00');
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据仓库技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、题目背景
  • 二、题目
  • 三、思路分析
  • 四、逐步推导
    • 步骤1:用 ROW_NUMBER 为每个用户的订单按时间编号
    • 步骤2:将第1笔和第2笔时间提取到同一行
    • 步骤3:计算平均间隔
  • 五、常见坑点
  • 六、举一反三
  • 七、知识点总结
  • 八、建表语句和数据插入
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档