PostgreSQL计算2021年用户首尾发帖天数差的逻辑问题求助
PostgreSQL计算2021年用户首尾发帖天数差的逻辑问题求助
兄弟,我来帮你拆解下你的SQL逻辑里的问题,顺便给你几个更靠谱的解法:
先说说你当前代码里的核心问题
1. dense_rank的排序字段完全错了
你写的dense_rank() over(PARTITION BY user_id order by user_id desc),相当于在每个用户分组里,按user_id倒序排——但同一用户的user_id都是一样的啊!所以每个用户下的所有帖子,dense_rank的结果全是1,那where n <=2等于没过滤,自然会保留所有行,这就是你看到每个用户有多个结果的原因。
你应该按post_date排序,才能区分出每个用户的最早和最晚帖子。
2. lag函数没有按用户分区
你的lag(post_date::date) over (order by user_id)是全局排序后取上一行,这会把不同用户的日期混在一起计算。比如前一行是用户A的晚日期,当前行是用户B的早日期,减出来自然是负数,完全不符合你要的“同一用户首尾帖差”的逻辑。
3. where n >=2没结果的原因
因为前面的dense_rank给所有行的标记都是1,自然没有行满足n >=2,所以返回空结果。
给你两种更简洁的正确解法
解法一:用聚合函数直接计算(最推荐)
这种方法最简单直接,利用MIN和MAX分别取每个用户2021年的最早、最晚发帖日期,然后相减得到天数差:
SELECT user_id, (MAX(post_date::date) - MIN(post_date::date)) AS days_between FROM posts WHERE EXTRACT(YEAR FROM post_date) = 2021 GROUP BY user_id -- 可选:如果要过滤只有1条帖子的用户(这类用户天数差为0),可以加下面这行 -- HAVING COUNT(*) > 1 ORDER BY user_id;
这个方法会直接得到你想要的示例结果:
| user_id | days_between |
|---|---|
| 151652 | 2 |
| 661093 | 21 |
解法二:用窗口函数标记首尾帖再计算
如果你坚持想用窗口函数的思路,可以先给每个用户的首尾帖打标记,再聚合计算:
WITH user_post_marks AS ( SELECT user_id, post_date::date, -- 标记每个用户的最早帖子(升序排第1) ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY post_date ASC) AS rn_first, -- 标记每个用户的最晚帖子(降序排第1) ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY post_date DESC) AS rn_last FROM posts WHERE EXTRACT(YEAR FROM post_date) = 2021 ), first_last_posts AS ( SELECT user_id, post_date FROM user_post_marks WHERE rn_first = 1 OR rn_last = 1 ) SELECT user_id, MAX(post_date) - MIN(post_date) AS days_between FROM first_last_posts GROUP BY user_id ORDER BY user_id;
再回头解答你的几个具体疑问
- 为什么
n<=2会有多个结果?因为你的dense_rank排序字段错误,所有行的rank都是1,过滤条件等于无效。 - 为什么会有负数?因为
lag没有按用户分区,跨用户计算了日期差,导致晚日期减早日期变成了早日期减晚日期。 - 为什么
n>=2没结果?因为所有行的rank都是1,没有行满足这个条件。
备注:内容来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

