订阅更新

每个用户最近第二次活动:只有一次就返回那一次

SQL 实战 123 题 · 第 100 题 困难 约 2 分钟读完

LeetCode 1369 · 倒序排名、窗口计数、边界处理

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

面试怎么回答

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,只有一次活动的用户会被漏掉。
刷题进度保存在当前浏览器