订阅更新

连续登录 5 天的活跃用户:先去重再看间隔

SQL 实战 123 题 · 第 111 题 中等 约 2 分钟读完

LeetCode 1454 · 连续登录、去重、LEAD 看第 N 行

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

先按用户和日期去重,然后 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。
刷题进度保存在当前浏览器