每个用户最近第二次活动:只有一次就返回那一次
LeetCode 1369 · 倒序排名、窗口计数、边界处理
面试怎么回答
ROW_NUMBER() OVER (PARTITION BY username ORDER BY startDate DESC) 取 rn = 2;同时用 COUNT(*) OVER (PARTITION BY username) 拿到活动总数,总数为 1 的直接保留。条件写成 rn = 2 OR cnt = 1,边界情况一目了然。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
用户活动表记录每个用户每次活动的名称和起止日期(同一用户的活动不会重叠)。对每个用户,返回他最近的倒数第二次活动;如果只有一次活动,就返回这一次。
表结构
- UserActivity:username、activity、startDate、endDate;没有主键
思路
按用户分区、开始日期倒序编号,编号 2 就是倒数第二次。只有一次活动的用户没有编号 2,需要额外保留:原解法用 LAG 判断「没有前一次」;更直观的是同时算出每个用户的活动总数,条件写成「编号为 2,或者总数为 1」。
参考解法
select username, activity ,startDate,endDate
from
(
select username, activity ,startDate,endDate ,
rank()over(partition by username order by startDate desc) rk,
lag( startDate ,1,null)over(partition by username order by startDate ) lg
from UserActivity
)t1
where rk=2 or (rk = 1 && lg is null)
另一种写法:用窗口计数判断只有一次活动的用户
SELECT username, activity, startDate, endDate
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY username ORDER BY startDate DESC) AS rn,
COUNT(*) OVER (PARTITION BY username) AS cnt
FROM UserActivity
) t
WHERE rn = 2 OR cnt = 1
易错点
- 只取编号 2,只有一次活动的用户会被漏掉。