其实MS SQL Server函数用法挺多的,这里说说两种比较常见但容易搞混的分析方法
- 问答
- 2026-01-04 10:43:13
- 19
数据库开发社区常见讨论及SQL Server官方文档基础部分)
很多人用SQL Server做数据分析时,最头疼的不是写不出查询,而是面对一堆功能相似的函数不知道该选哪个,你想给查询结果里的每一行都加一个序号,或者想计算每一行数据占它所在分组的总和的比例,这时候就会碰到ROW_NUMBER()、RANK()、DENSE_RANK()和PARTITION BY这些看起来很像的东西,它们都属于“窗口函数”大家庭,但用起来差别不小,一不小心就会搞混,导致结果完全不对,下面我就用大白话聊聊其中两种特别容易混淆的分析方法:一种是纯粹排序编序号,另一种是带分组的分层计算。
第一种方法:给数据行排序和编序号(来源:SQL Server技术博客常见入门教程)
这主要涉及ROW_NUMBER()、RANK()和DENSE_RANK()这三个函数,它们的核心任务都是在指定的排序规则下,给每一行数据分配一个数字序号,但分配规则有微妙的不同,直接影响最终序号的样子。
-
ROW_NUMBER():老老实实排队,绝不并列。 这个函数最简单直接,你告诉它按哪个字段排序(比如按成绩从高到低),它就严格地按照这个顺序,从1开始,1、2、3、4……一路往下排,即使有两行数据的排序字段值一模一样(比如两个学生都是95分),ROW_NUMBER()也会强制给它们不同的序号(比如一个第3名,一个第4名),这个顺序可能取决于数据在底层存储的细微差别,总之它不允许出现并列名次,当你只是需要一个绝对的、唯一的序号,不关心是否并列时,就用它。
-
RANK():允许并列,但会“跳号”。 RANK()就有人情味一些了,如果遇到排序值相同的行,它会给这些相同的行分配同样的序号,有两个学生并列第一,RANK()都会给他们标1,下一个不同成绩的学生,序号就不是2了,而是3,因为第一名占了两个位置,所以它就“跳”过了2这个数字,这在体育比赛排名中很常见,并列冠军之后,下一个就是季军了。
-
DENSE_RANK():允许并列,且坚决不“跳号”。 DENSE_RANK()可以看作是RANK()的“改良版”,它同样处理并列情况(两个95分都排第1),但区别在于,它不会跳号,并列第一之后,下一个不同成绩的学生直接就是第2名,它只关心 distinct(不同)的排序值有多少个,序号就紧凑地往下排。

简单总结一下区别:假设成绩是100, 95, 95, 90。
- ROW_NUMBER() 会给出:1, 2, 3, 4
- RANK() 会给出:1, 2, 2, 4 (注意,两个95分并列第二,下一个90分就是第四名)
- DENSE_RANK() 会给出:1, 2, 2, 3 (两个95分并列第二,下一个90分是第三名)
第二种方法:在分组内进行分析计算(来源:MSDN官方文档窗口函数概述部分)
光会排序还不够,很多时候我们需要把数据先分成几个小组,然后在每个小组内部进行计算,这就是PARTITION BY子句大显身手的时候了,它通常和上面说的排序函数,或者和SUM()、AVG()这样的聚合函数一起用。
-
PARTITION BY 的作用:化整为零,分组计算。 你可以把PARTITION BY想象成SQL里的“分组符”,但它和GROUP BY不同,GROUP BY会把多行数据压缩成一行汇总结果,而PARTITION BY不会减少行数,它只是在计算时,先把数据按照你指定的字段(比如部门、班级、年份)分成一个个独立的小组,然后在每个小组内部单独执行排序或聚合操作,最后再把结果合并回原来的数据集里展示。

-
经典组合:PARTITION BY + 排序函数/聚合函数 你有一个销售表,有“部门”和“销售额”字段,你想看看每个员工在自己部门内的销售排名。 你可以写:
SELECT 部门, 员工姓名, 销售额, RANK() OVER (PARTITION BY 部门 ORDER BY 销售额 DESC) AS 部门内排名 FROM 销售表。 这个查询的意思就是:先按照“部门”把数据分成若干组(比如A部一组,B部一组),然后在每个部门内部,再按照销售额从高到低用RANK()函数进行排名,这样,结果里A部的第一名是1,B部的第一名也是1,互不干扰,如果没有PARTITION BY,就会在全公司范围内排名,A部和B部的人会混在一起排。再比如,你想计算每个员工销售额占其所在部门总销售额的比例。 可以写:
SELECT 部门, 员工姓名, 销售额, 销售额 * 1.0 / SUM(销售额) OVER (PARTITION BY 部门) AS 销售占比 FROM 销售表。 这里,SUM(销售额) OVER (PARTITION BY 部门)的意思就是,针对每一行数据,计算它所在“部门”的所有行的销售额总和,这个总和值会附加在每一行后面,然后你就可以用当前行的销售额除以这个部门总和,得到占比。
来源:个人经验归纳)
区分这两种方法的关键在于:
- ROW_NUMBER()、RANK()、DENSE_RANK() 这几个函数核心解决的是“排序编号”问题,区别在于对待并列成绩时的编号策略不同。
- PARTITION BY 核心解决的是“分组计算”问题,它像一个透明的玻璃墙,把数据隔成多个小房间,让窗口函数在各自的小房间里工作,互不影响。
在实际工作中,它们经常结合在一起使用,先由PARTITION BY划定分析范围(在哪个组里算),再由ORDER BY决定组内的顺序,最后由具体的窗口函数(编号的或聚合的)执行计算,弄明白它们各自的分工和合作方式,写起复杂分析SQL来就会清晰很多。
本文由颜泰平于2026-01-04发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/74278.html
