分组聚合时仅用用户最新值的PostgreSQL优雅SQL实现问询
优雅解决PostgreSQL用户最新记录的灵活聚合需求
嘿,这个需求用PostgreSQL的窗口函数就能轻松搞定,比你说的复杂关联写法清爽太多,而且灵活性拉满!
核心思路:先锁定每个用户的最新记录
首先我们用ROW_NUMBER()窗口函数给每个用户的记录按时间倒序排号,最新的那条记录会被标记为1。这一步是固定的,不管后续怎么聚合,只要拿到每个用户的最新记录,剩下的操作就非常灵活了。
灵活聚合的实现代码
第一步:获取每个用户的最新记录(通用部分)
我们可以用CTE(公共表表达式)把这部分逻辑抽出来,方便后续复用:
WITH user_latest_records AS ( SELECT user_id, vals, created_at, -- 按用户分组,按创建时间倒序排,最新的记录行号为1 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS record_rank FROM your_table_name )
第二步:按需选择聚合方式
场景1:按月份分组,计算最新记录的vals总和
只需要在CTE基础上筛选出最新记录,再按月份分组求和:
WITH user_latest_records AS ( SELECT user_id, vals, created_at, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS record_rank FROM your_table_name ) SELECT DATE_TRUNC('month', created_at) AS month, -- 把时间截断到月份 SUM(vals) AS total_vals FROM user_latest_records WHERE record_rank = 1 -- 只保留每个用户的最新记录 GROUP BY month ORDER BY month;
场景2:按user_id分组,查看每个用户最新记录的vals(或其他聚合)
如果要切换成按用户分组,只需要修改外层的GROUP BY和聚合逻辑,完全不用动CTE部分:
WITH user_latest_records AS ( SELECT user_id, vals, created_at, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS record_rank FROM your_table_name ) SELECT user_id, vals AS latest_val, -- 直接取最新记录的vals created_at AS latest_record_time FROM user_latest_records WHERE record_rank = 1 ORDER BY user_id;
额外优化:处理同一用户多条“最新”记录的情况
如果你的业务中存在同一个用户在同一时间有多条记录(比如created_at完全相同),并且这些记录都需要被纳入计算,那可以把ROW_NUMBER()换成RANK(),这样所有并列最新的记录都会被标记为1,不会被过滤掉:
-- 替换CTE里的行号函数 RANK() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS record_rank
这种写法的好处就是逻辑分层清晰,取最新记录的逻辑和后续聚合逻辑完全解耦,想换聚合方式只需要改外层查询,非常灵活,而且可读性比复杂的关联写法高太多!
内容的提问来源于stack exchange,提问作者eljusticiero67
相关产品推荐
相关产品推荐

