这是牛客SQL大厂真题精选第40题,属于‘窗口函数 + 日期处理’的综合考察题
通过本题你可以学到LEAD窗口函数的核心技巧
目录
一、题目简述
题目要求:
请查询连续登陆不少于3天的新注册用户?要求:输出user_id并升序排序。
注:登录表为单日随机一次登录数据,该题忽略单日多次登录情况。
考察重点:
- 窗口函数LEAD的使用
- 日期函数DATEDIFF的使用
二、题目分析
1.题意解析
原文描述:
某产品在2022年2月8日各端口用户注册信息及后几日登录信息如下:
用户注册信息表register_tb(user_id-用户id, reg_time-注册时间, reg_port-注册端口)
用户登录信息表login_tb(log_id-登录动作id,user_id-用户id, log_time-登录时间, log_port-登录端口)
问题:请查询连续登陆不少于3天的新注册用户?要求:输出user_id并升序排序。
注:登录表为单日随机一次登录数据,该题忽略单日多次登录情况。
- 条件1:连续登录>=3天的用户 --> login_tb表 (log_time字段)
- 条件2:输出user_id 并按user_id升序排序 --> login_tb表、register_tb表 (user_id字段)
- 条件3:注:登录表为单日.....每个用户一天只有一条登录记录(无需去重)
“综上,本题要求找出 register_tb 表中注册且在 login_tb 表中出现连续三天登录记录的用户”
2.观察表结构及字段
2.1输入数据:
login_tb表(log_time字段)为日期类型,可使用日期类函数
login_tb表(user_id字段)与 register_tb表(user_id字段)关联,可筛选新注册的用户
2.2输出数据:
user_id:用户id (此id既满足存在3天连续登录且在register_tb表)
| user_id |
| 1102 |
| 1106 |
三、解法思路
3.1方法
在 login_tb 表使用LEAD()获取当前行后两行的 log_time 值(log_time2,log_time3),(详见3.2 Step1 表格数据示例)。再通过DATEDIFF()判断三天是否连续(均为1)。 最后关联 register_tb表筛选新注册用户。
3.2重点步骤
Step1:LEAD窗口函数取后两行数据
lead(log_time,1) over(partition by user_id order by log_time) log_time_2,
lead(log_time,2) over(partition by user_id order by log_time) log_time_3
| user_id | log_time | log_time_2 | log_time_3 |
|---|---|---|---|
| 1003 | 2022-02-09 09:36:11 | 2022-02-10 10:43:01 | 2022-02-11 17:43:01 |
| 1003 | 2022-02-10 10:43:01 | 2022-02-11 17:43:01 | NULL |
| 1003 | 2022-02-11 17:43:01 | NULL | NULL |
| 1101 | 2022-02-09 07:24:15 | NULL | NULL |
| 1102 | 2022-02-09 09:12:57 | 2022-02-10 09:37:01 | 2022-02-11 11:56:47 |
| 1102 | 2022-02-10 09:37:01 | 2022-02-11 11:56:47 | NULL |
| 1102 | 2022-02-11 11:56:47 | NULL | NULL |
| 1104 | 2022-02-10 12:01:46 | 2022-02-11 14:52:37 | NULL |
| 1104 | 2022-02-11 14:52:37 | NULL | NULL |
| 1106 | 2022-02-10 10:23:01 | 2022-02-11 16:56:27 | 2022-02-12 10:56:17 |
| 1106 | 2022-02-11 16:56:27 | 2022-02-12 10:56:17 | NULL |
| 1106 | 2022-02-12 10:56:17 | NULL | NULL |
Step2:DATEDIFF函数进行时间相减
where datediff(log_time_2,log_time) = 1 and datediff(log_time_3,log_time_2) = 1
如果结果 = 1,说明两天是连续的
Step3: 对Step1、Step2处理得到的user_id进行去重(可做可不做 不影响答案正确性)
若同一用户存在多段连续登录,结果中会重复出现,可使用DISTINCT去重,但不影响最终输出结果。
Step4:两表通过user_id关联
register_tb r
join
users u
on
r.user_id = u.user_id
四、完整MySQL实现代码
with login_seq as(
-- 使用 LEAD() 构造后两日登录时间
select
user_id,
log_time,
lead(log_time,1) over(partition by user_id order by log_time) log_time_2,
lead(log_time,2) over(partition by user_id order by log_time) log_time_3
from
login_tb
),
valid_users as(
-- 筛选连续三天登录的用户
select
-- distinct
user_id
from
login_seq
where datediff(log_time_2,log_time) = 1
and datediff(log_time_3,log_time_2) = 1
)
# 关联注册表,输出最终结果
select r.user_id
from register_tb r
join valid_users v on r.user_id = v.user_id
order by r.user_id
五、易错点
| 错误写法 | 问题说明 | 正确写法 |
| LAD() | 函数名写错,应为 LEAD() | LEAD(log_time,1) OVER(...) |
| log_time2 - log_time | 直接相减返回NULL | 使用DATEDIFF比较日期差 |
六、面试延伸(重点)
延伸一:讲原理(展示思维)
我使用LEAD()获取后一行的日期,再用DATEDIFF()判断是否相差 一天。
连续登录其实是一个“时间序列分段问题”,可以用窗口函数或自连接实现
延伸二:讲可迁移性(展示经验)
这种连续性判断思路,不止能用在登录分析,还能用在:
1.连续下单天数
2.连续访问天数
3.连续订阅续费分析
延伸三:讲优化(展示深度)
“如果数据量大,可以先按 user_id + login_date 建索引,
或者使用窗口函数预聚合后再过滤,减少自连接开销。”
七、总结
本题是窗口函数处理时间序列问题的典型例题,掌握后可举一反三
以下是核心内容总结,可作复习使用。
| 知识点 | 内容 |
|---|---|
| 核心函数 | LEAD()、DATEDIFF() |
| 常见场景 | 连续登录、连续购买、连续签到 |
| 延伸技巧 | 序列分组、时间窗口聚合 |
| 面试话术 | “我先用窗口函数找相邻记录,再用时间差判断连续性” |
浙公网安备 33010602011771号