连续登录 5 天的活跃用户:先去重再看间隔
LeetCode 1454 · 连续登录、去重、LEAD 看第 N 行
面试怎么回答
先按用户和日期去重,然后 LEAD(login_date, 4) OVER (PARTITION BY id ORDER BY login_date),和当前日期相差 4 天就是连续 5 天。通用写法是「日期减行号」得到连续段编号,按段计数 >= n。去重是这道题最容易漏的一步。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
账号表和登录表(一天可能登录多次)。找出至少连续 5 天都登录过的用户,按 ID 排序;思考如何推广到连续 n 天。
表结构
- Accounts:id(主键)、name
- Logins:id、login_date;没有主键,可能有重复行
思路
先按(用户, 日期)去重,一天多次登录只算一次。然后对每个用户按日期排序,用 LEAD 取后面第 4 条记录的日期:如果它和当前日期正好相差 4 天,说明这 5 条记录是 5 个连续的日子。推广到 n 天,就是 LEAD(login_date, n-1) 且相差 n-1 天;也可以用「日期减行号」分组,统计每段长度。
参考解法
select t3.id,name
from
(
select distinct id
from
(
select id,login_date,lead(login_date,4,null) over(partition by id order by login_date) ld
from
(
select id,login_date
from Logins
group by id,login_date
)t1
)t2
where datediff(ld,login_date)=4
)t3
left join Accounts a
on t3.id = a.id
另一种写法:日期减行号分组,适合任意 n
SELECT DISTINCT a.id, a.name
FROM (
SELECT id, DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY id ORDER BY login_date) DAY) AS grp
FROM (SELECT DISTINCT id, login_date FROM Logins) d
) t
JOIN Accounts a ON a.id = t.id
GROUP BY a.id, a.name, t.grp
HAVING COUNT(*) >= 5
ORDER BY a.id
易错点
- 不先去重,一天多次登录会让 LEAD 取到同一天,间隔判断就错了。
- 同一用户可能满足多次,结果要 DISTINCT。