SQL窗口函数完全指南:ROW_NUMBER、RANK、DENSE_RANK实战详解

张开发
2026/4/14 23:49:04 15 分钟阅读

分享文章

SQL窗口函数完全指南:ROW_NUMBER、RANK、DENSE_RANK实战详解
SQL窗口函数完全指南ROW_NUMBER、RANK、DENSE_RANK实战详解窗口函数是数据分析师从会SQL到精通SQL的分水岭。本文用真实业务场景把三个最常用的排名函数讲透。一、什么是窗口函数普通聚合函数如SUM、COUNT会把多行数据压缩成一行。而窗口函数不同它在每一行上计算但可以看到同一窗口内的其他行。结果行数不变但每行多了一列窗口内的计算结果。语法模板-- 窗口函数基本语法 函数名() OVER ( PARTITION BY 分组列 -- 按什么分组可选 ORDER BY 排序列 DESC -- 按什么排序 )二、三个排名函数的核心区别先用一个简单的例子看清楚区别-- 示例数据销售员业绩表 -- 姓名 部门 销售额 -- 小王 华南 100 -- 小李 华南 80 -- 小张 华南 80 ← 和小李并列 -- 小赵 华南 60 SELECT 姓名, 销售额, ROW_NUMBER() OVER (ORDER BY 销售额 DESC) AS row_num, RANK() OVER (ORDER BY 销售额 DESC) AS rank_num, DENSE_RANK() OVER (ORDER BY 销售额 DESC) AS dense_num FROM 销售表;输出结果姓名 销售额 row_num rank_num dense_num 小王 100 1 1 1 小李 80 2 2 2 小张 80 3 2 2 ← 同分同名次 小赵 60 4 4 3 ← 区别在这里三个函数的本质区别ROW_NUMBER()不管有没有并列每行给一个唯一序号。并列的也强行分开排。RANK()并列的给相同名次但下一个名次会跳过。小李小张都是第2下一个直接第4DENSE_RANK()并列的给相同名次下一个名次不跳过。小李小张都是第2下一个是第3三、ROW_NUMBER()最常用的去重神器场景1每个用户只取最新的一条订单-- 业务场景orders表有重复数据每个user_id保留最新的一条 SELECT * FROM ( SELECT user_id, order_id, order_time, amount, -- 按用户分组按时间倒序排名 ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY order_time DESC ) AS rn FROM orders ) t WHERE rn 1; -- 只取每个用户的第1条最新注意为什么不用RANK因为如果同一用户有两条完全相同时间的订单RANK会返回2条名次都是1而ROW_NUMBER严格只保留1条。场景2分页查询OFFSET/LIMIT的替代方案-- 获取销售额排名第11-20名的员工第2页 SELECT * FROM ( SELECT 员工姓名, 销售额, ROW_NUMBER() OVER (ORDER BY 销售额 DESC) AS rn FROM 员工表 ) t WHERE rn BETWEEN 11 AND 20;四、RANK()体育竞技的标准排名场景学生考试成绩排名-- 期末考试成绩排名允许并列符合真实考试规则 SELECT student_name, score, RANK() OVER ( PARTITION BY class_id -- 按班级分别排名 ORDER BY score DESC ) AS class_rank, RANK() OVER ( ORDER BY score DESC -- 全校排名 ) AS school_rank FROM exam_results WHERE subject 数学 ORDER BY class_id, class_rank;适用场景运动会名次、考试排名、竞赛排名——凡是并列后下一名跳过的场景用 RANK。五、DENSE_RANK()电商平台的销量排行榜场景商品销量榜连续显示名次-- 各品类TOP10商品连续排名不跳号 SELECT * FROM ( SELECT category, product_name, sales_qty, DENSE_RANK() OVER ( PARTITION BY category ORDER BY sales_qty DESC ) AS sales_rank FROM product_sales WHERE stat_date 2026-04-14 ) t WHERE sales_rank 10; -- 取每个品类销量前10为什么用 DENSE_RANK 而不是 RANK如果第2名有3个商品并列用 RANK 下一个是第5名用户看到第3名第4名空了体验不好。DENSE_RANK 确保显示1、2、3、4……连续名次。六、综合实战销售绩效分析-- 真实业务场景销售员月度绩效分析 -- 需求 -- 1. 每个大区内的销售额排名 -- 2. 同分员工并列名次用RANK -- 3. 标记每个大区前3名用于发奖金 -- 4. 为去除重复数据准备唯一序号用ROW_NUMBER WITH sales_ranked AS ( SELECT region, -- 大区 salesman, -- 销售员姓名 sales_amount, -- 销售额 -- 大区内排名允许并列跳号 RANK() OVER ( PARTITION BY region ORDER BY sales_amount DESC ) AS region_rank, -- 大区内连续排名允许并列不跳号 DENSE_RANK() OVER ( PARTITION BY region ORDER BY sales_amount DESC ) AS region_dense_rank, -- 全公司排名唯一序号 ROW_NUMBER() OVER ( ORDER BY sales_amount DESC ) AS company_row_num FROM sales_data WHERE month 2026-04 ) SELECT region, salesman, sales_amount, region_rank, CASE WHEN region_rank 1 THEN 第一名 WHEN region_rank 2 THEN 第二名 WHEN region_rank 3 THEN 第三名 ELSE 普通员工 END AS award_level FROM sales_ranked WHERE region_dense_rank 3 -- 取各大区前3名含并列 ORDER BY region, region_rank;七、常见坑和注意事项坑1PARTITION BY 忘了写-- ❌ 错误没有PARTITION BY全表作为一个窗口排名 SELECT ROW_NUMBER() OVER (ORDER BY sales DESC) AS rn FROM sales; -- ✅ 正确按部门分别排名 SELECT ROW_NUMBER() OVER (PARTITION BY dept ORDER BY sales DESC) AS rn FROM sales;坑2WHERE 里不能直接用窗口函数-- ❌ 错误直接在WHERE中用窗口函数 SELECT * FROM orders WHERE ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY time DESC) 1; -- ✅ 正确套一层子查询或CTE SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY time DESC) AS rn FROM orders ) t WHERE rn 1;坑3RANK 和 DENSE_RANK 的选择依赖业务含义取前N名时业务上要不要包含并列情况决定用哪个前3名发奖金用DENSE_RANK() 3并列第3也发只取前3条记录用ROW_NUMBER() 3强制只取3条八、速查表函数并列处理名次跳跃典型场景ROW_NUMBER()强制唯一无跳跃去重、分页RANK()相同名次会跳跃体育竞技、考试DENSE_RANK()相同名次不跳跃电商排行榜、连续名次总结三个函数记住一句话ROW_NUMBER给每行一个不重复的门牌号RANK奥运会规则并列第2后下一个是第4DENSE_RANK电商榜规则并列第2后下一个是第3选哪个看业务需求要唯一用ROW_NUMBER要允许并列看要不要跳号选RANK或DENSE_RANK。船长Talk| 数据分析师专注SQL实战教程。关注公众号「船长Talk」持续更新数据分析技能干货。

更多文章