如何查询每个用户的倒数第二个活动(单活动时输出该活动)
解决方案
样本数据
| Username | Activity | Start Time | End Time |
|---|---|---|---|
| Ace | Dancing | 13:00 | 14:00 |
| Ace | Singing | 15:00 | 16:30 |
| Ace | Yoga | 19:00 | 20:00 |
| Alice | Piano | 10:00 | 11:00 |
| Alice | Hiking | 14:00 | 15:00 |
| Alice | Reading | 16:00 | 16:30 |
| Alice | Swimming | 19:00 | 20:00 |
| Alice | Writing | 21:00 | 21:30 |
| Lion | Fishing | 13:00 | 17:00 |
需求
查询每个用户的倒数第二个活动,若用户仅有一个活动,则输出该活动,期望结果:
| Username | Penultimate_Act |
|---|---|
| Ace | Singing |
| Alice | Swimming |
| Lion | Fishing |
问题分析
你之前的SQL存在两个问题:
- 第一个查询只筛选
seq=2,但Lion只有1个活动,对应的seq=1,无法被选中; - 第二个查询筛选
seq=1 OR seq=2,会返回每个用户的最新2个活动(比如Ace会返回Yoga和Singing),不符合仅返回倒数第二个(或唯一活动)的要求。
另外,DENSE_RANK()并非最优选择,若同一用户存在同一时间的多个活动,它会给这些活动分配相同排名,可能导致结果偏离预期。更适合用ROW_NUMBER()为每个用户的活动按时间倒序生成唯一序号。
正确SQL(PostgreSQL)
SELECT username, activity AS penultimate_act FROM ( SELECT username, activity, ROW_NUMBER() OVER (PARTITION BY username ORDER BY "Start Time" DESC) AS seq, COUNT(*) OVER (PARTITION BY username) AS total_acts FROM activities ) t WHERE (total_acts = 1 AND seq = 1) OR (total_acts > 1 AND seq = 2);
逻辑解释
ROW_NUMBER() OVER (PARTITION BY username ORDER BY "Start Time" DESC):按用户分组,将每个用户的活动按开始时间倒序排列,生成唯一序号seq(最新活动为1,倒数第二为2);COUNT(*) OVER (PARTITION BY username):统计每个用户的活动总数total_acts;- 外层筛选:若用户只有1个活动则取
seq=1,若活动数大于1则取seq=2,完全匹配需求。
内容的提问来源于stack exchange,提问作者Meliodas Dragon
相关产品推荐
相关产品推荐

