当前位置: 首页 > news >正文

PostgreSQL ROW_NUMBER() 窗口函数完全解析

一、什么是"窗口函数"?(大白话版)

“窗口"不是窗户,而是"视野范围”!

  • 普通聚合函数(SUM、COUNT):把多行压缩成一行,你看不到原始数据了
  • 窗口函数(ROW_NUMBER):在每行旁边附加计算结果,原始数据还在

比喻:

普通聚合:把全班成绩汇总成平均分 → 你看不出每个人的分数 窗口函数:在每个人旁边标注"班级第几名" → 既看到分数,又看到排名

为什么叫"窗口"?
因为你可以定义一个"滑动窗口"(比如"当前行 + 前2行"),在这个范围内计算。


二、ROW_NUMBER() 一句话解释

给每组数据编个号:1、2、3、4…,从 1 开始连续递增。


三、9 个最实用场景

场景 1:去重(保留最新/最早的一条)

需求:用户可能有多条订单,只保留每个用户的最新订单

SELECT*FROM(SELECTuser_id,order_no,created_at,ROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYcreated_atDESC)ASrnFROMorders)tWHERErn=1;-- 只取每个用户的第一条(最新的)

原理:

  • PARTITION BY user_id:按用户分组
  • ORDER BY created_at DESC:每组内按时间倒序
  • ROW_NUMBER():编号 1、2、3…
  • WHERE rn = 1:只要第一条

场景 2:分页查询(高效分页)

需求:查询第 11-20 条记录

SELECT*FROM(SELECTid,name,created_at,ROW_NUMBER()OVER(ORDERBYcreated_atDESC)ASrnFROMusers)tWHERErnBETWEEN11AND20;

优势:LIMIT/OFFSET在大数据量时更快(尤其是深分页)


场景 3:找出每组的前 N 名

需求:每个部门工资最高的 3 个人

SELECT*FROM(SELECTdept_name,emp_name,salary,ROW_NUMBER()OVER(PARTITIONBYdept_nameORDERBYsalaryDESC)ASrnFROMemployees)tWHERErn<=3;-- 每个部门前 3 名

场景 4:删除重复数据

需求:清理重复的用户记录,只保留 ID 最小的

DELETEFROMusersWHEREidIN(SELECTidFROM(SELECTid,email,ROW_NUMBER()OVER(PARTITIONBYemailORDERBYidASC)ASrnFROMusers)tWHERErn>1-- 保留 rn=1 的,删除其他的);

场景 5:对比当前行和上一行

需求:计算每日销售额环比增长

SELECTsale_date,daily_amount,LAG(daily_amount)OVER(ORDERBYsale_date)ASprev_day_amount,ROUND((daily_amount-LAG(daily_amount)OVER(ORDERBYsale_date))/LAG(daily_amount)OVER(ORDERBYsale_date)*100,2)ASgrowth_rateFROMdaily_sales;

注意:这里用LAG()更适合,但ROW_NUMBER()也可以实现类似效果。


场景 6:标记首次/最后一次行为

需求:标记用户的首次登录和最后登录

SELECTuser_id,login_time,CASEWHENROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYlogin_timeASC)=1THEN'首次登录'WHENROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYlogin_timeDESC)=1THEN'最后登录'ELSE'普通登录'ENDASlogin_typeFROMuser_logins;

场景 7:分组后取中间值

需求:去掉最高分和最低分,取中间的平均分

SELECTstudent_id,AVG(score)ASavg_scoreFROM(SELECTstudent_id,score,ROW_NUMBER()OVER(PARTITIONBYstudent_idORDERBYscoreASC)ASrn_asc,ROW_NUMBER()OVER(PARTITIONBYstudent_idORDERBYscoreDESC)ASrn_desc,COUNT(*)OVER(PARTITIONBYstudent_id)AStotal_countFROMexam_scores)tWHERErn_asc>1ANDrn_desc>1;-- 去掉最低和最高

场景 8:检测数据连续性

需求:找出用户连续登录的天数

SELECTuser_id,login_date,login_date-(ROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYlogin_date)||' days')::INTERVALASgrpFROMuser_loginsGROUPBYuser_id,login_date;-- 相同的 grp 表示连续登录SELECTuser_id,MIN(login_date)ASstart_date,MAX(login_date)ASend_date,COUNT(*)ASconsecutive_daysFROM(-- 上面的子查询)tGROUPBYuser_id,grpHAVINGCOUNT(*)>=3;-- 连续登录 3 天以上

场景 9:排行榜(带并列处理)

需求:生成销售排行榜,相同业绩排名相同

-- ROW_NUMBER():即使分数相同,排名也不同(1、2、3、4)SELECTemp_name,sales_amount,ROW_NUMBER()OVER(ORDERBYsales_amountDESC)ASrankFROMsales_performance;-- 如果需要并列排名,用 RANK() 或 DENSE_RANK()-- RANK(): 1, 2, 2, 4 (跳过 3)-- DENSE_RANK(): 1, 2, 2, 3 (不跳过)

四、核心语法拆解

ROW_NUMBER()OVER(PARTITIONBYcolumn1,column2-- 可选:分组依据ORDERBYcolumn3DESC-- 必填:排序规则)

三个关键点:

  1. OVER():声明这是窗口函数
  2. PARTITION BY:可选,类似GROUP BY,但不压缩行数
  3. ORDER BY:必填,决定编号顺序

五、ROW_NUMBER vs RANK vs DENSE_RANK

函数相同值处理示例适用场景
ROW_NUMBER()强制不同1, 2, 3, 4去重、分页
RANK()并列,跳号1, 2, 2, 4排行榜(允许空缺)
DENSE_RANK()并列,不跳号1, 2, 2, 3排行榜(紧凑排名)

示例对比:

SELECTname,score,ROW_NUMBER()OVER(ORDERBYscoreDESC)ASrow_num,RANK()OVER(ORDERBYscoreDESC)ASrank,DENSE_RANK()OVER(ORDERBYscoreDESC)ASdense_rankFROMstudents;-- 结果:-- name | score | row_num | rank | dense_rank-- ------+-------+---------+------+------------- 张三 | 100 | 1 | 1 | 1-- 李四 | 100 | 2 | 1 | 1 ← 并列第一-- 王五 | 95 | 3 | 3 | 2 ← RANK 跳过 2,DENSE 不跳-- 赵六 | 90 | 4 | 4 | 3

六、性能优化建议

1. 避免全表扫描

-- ❌ 慢:全表编号后再过滤SELECT*FROM(SELECT*,ROW_NUMBER()OVER(ORDERBYcreated_at)ASrnFROMorders)tWHERErn<=10;-- ✅ 快:先过滤再编号SELECT*,ROW_NUMBER()OVER(ORDERBYcreated_at)ASrnFROMordersWHEREcreated_at>='2026-01-01'ORDERBYcreated_atLIMIT10;

2. 合理使用索引

-- 为 PARTITION BY 和 ORDER BY 字段创建索引CREATEINDEXidx_orders_user_createdONorders(user_id,created_atDESC);-- 这样查询会很快SELECT*FROM(SELECT*,ROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYcreated_atDESC)ASrnFROMorders)tWHERErn=1;

3. 避免不必要的 PARTITION BY

-- ❌ 如果不需要分组,不要加 PARTITION BYROW_NUMBER()OVER(PARTITIONBY1ORDERBYid)-- 多余!-- ✅ 直接全局编号ROW_NUMBER()OVER(ORDERBYid)

七、常见错误

错误 1:忘记 ORDER BY

-- ❌ 错误:窗口函数必须包含 ORDER BYROW_NUMBER()OVER(PARTITIONBYuser_id)-- ✅ 正确ROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYcreated_at)

错误 2:在 WHERE 中直接使用

-- ❌ 错误:窗口函数不能在 WHERE 中使用SELECT*FROMordersWHEREROW_NUMBER()OVER(ORDERBYid)=1;-- ✅ 正确:用子查询SELECT*FROM(SELECT*,ROW_NUMBER()OVER(ORDERBYid)ASrnFROMorders)tWHERErn=1;

错误 3:误解 PARTITION BY

-- ❌ 错误理解:以为 PARTITION BY 会分组返回SELECTuser_id,COUNT(*)FROMordersGROUPBYuser_id;-- 这才是分组-- ✅ 正确理解:PARTITION BY 不减少行数SELECTuser_id,ROW_NUMBER()OVER(PARTITIONBYuser_idORDERBYid)ASrnFROMorders;-- 行数不变,只是加了编号

八、记忆口诀

ROW_NUMBER 编序号,分组排序不能少 去重分页最常用,子查询里套一层 PARTITION 是分堆,ORDER 决定谁在前 WHERE 不能直接调,外层过滤才正确

九、总结

核心要点

  1. 窗口函数= 在不压缩行的前提下,附加计算结果
  2. ROW_NUMBER()= 给每组数据编连续序号(1、2、3…)
  3. 最常用场景= 去重、分页、取前 N 名
  4. 必须配合=OVER()+ORDER BY
  5. 使用时机= 需要"组内排名"或"唯一标识"时

快速参考

-- 基本模板SELECT*FROM(SELECT字段列表,ROW_NUMBER()OVER(PARTITIONBY分组字段-- 可选ORDERBY排序字段DESC-- 必填)ASrnFROM表名)tWHERErn=1;-- 或其他条件

http://www.jsqmd.com/news/1066144/

相关文章:

  • 一线观察:长期体验后西安GEO优化公司的真实适配边界 - GrowthUME
  • 2026深圳靠谱装修公司盘点 覆盖新房整装、老房翻新与别墅全案 - GrowthUME
  • 2026在线水印去除怎么做?免费工具合集+安全无风险图文实操教程
  • Sunshine游戏串流终极指南:如何打造你的私人云游戏服务器
  • OpenClaw不是GPT-5.4:AI工作流编排的真相与实战
  • OpenClaw:Windows本地AI Agent运行时与Skill编排系统
  • 武当山风景区热门的武校哪家强 - GrowthUME
  • 2026年潍坊企业做网站建设怎么选?找正规源头服务商更省心靠谱 - GrowthUME
  • console-powers源码解析:理解控制台输出的底层原理
  • 5分钟快速上手qtmodern:为你的Python GUI应用添加无边框窗口
  • 如何使用gh_mirrors/su/subcommands快速构建功能强大的Go CLI应用
  • 有实力的汽车贴改色膜企业,博斐汽车贴膜值得选 - mypinpai
  • 2026呼伦贝尔黑头山游玩攻略:访牧户必体验项目与避坑指南,首选美丽草原访牧户不踩坑 - GrowthUME
  • 绍兴管道疏通/绍兴附近上门疏通真实测评(2026新)口碑推荐绍兴泓畅管道疏通 - GrowthUME
  • 在 C# 中,异步任务取消机制是异步编程中处理任务中断的核心功能,广泛应用于需要响应用户操作、超时或外部条件终止任务的场景
  • 2026威海系统门窗选购指南:五大品牌技术实测与气候适配分析 - Gsydold
  • AI API中转站:统一OpenAI接口调用600+模型的工程实践
  • Hakawai 性能优化指南:解决 iOS 文本视图的常见性能瓶颈
  • 国内主流人事系统实测对比 助力企业人力数字化升级 - 得赢
  • ASL预训练模型大揭秘:TResNet系列如何刷新MS-COCO榜单
  • 深圳横岗眼镜城配镜避坑指南|对标眼科标准专业验光,瞬乐视眼视光中心(横岗眼镜城店) 全流程实测记录 - GrowthUME
  • Mistral Medium 3.5:生产级稠密模型驱动的远程编码Agent
  • 汽车贴改色膜性价比高的品牌,博斐汽车贴膜口碑佳 - mypinpai
  • 义乌管道疏通哪家口碑好?2026年义乌伟杰疏通值得信赖-承接家庭疏通马桶/疏通下水道/清理化粪池 - GrowthUME
  • 软件测试|电商类项目业务测试点汇总
  • RuoYi-Cloud-Vue微服务落地实战:Nacos、Sentinel、Seata深度排障指南
  • B站会员购抢票神器终极指南:三步配置零基础快速上手biliTickerBuy
  • 人才测评系统选型升温:行业共识锚定五大核心标准 - 得赢
  • 汽车贴改色膜机构推荐,博斐汽车贴膜口碑好 - mypinpai
  • DownKyi终极指南:轻松实现B站8K超高清视频批量下载与高效管理