排名函数

排名函数是Sql Server2005新增的功能,下面简单介绍一下他们各自的用法和区别。我们新建一张Order表并添加一些初始数据方便我们查看效果。

CREATE TABLE [dbo].[Order](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [UserId] [int] NOT NULL,
    [TotalPrice] [int] NOT NULL,
    [SubTime] [datetime] NOT NULL,
 CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET IDENTITY_INSERT [dbo].[Order] ON 

GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (1, 1, 100, CAST(0x0000A419011D32AF AS DateTime))
GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (2, 2, 500, CAST(0x0000A419011D40BA AS DateTime))
GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (3, 3, 300, CAST(0x0000A419011D4641 AS DateTime))
GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (4, 2, 1000, CAST(0x0000A419011D4B72 AS DateTime))
GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (5, 1, 520, CAST(0x0000A419011D50F3 AS DateTime))
GO
INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (6, 2, 2000, CAST(0x0000A419011E50C9 AS DateTime))
GO
SET IDENTITY_INSERT [dbo].[Order] OFF
GO
ALTER TABLE [dbo].[Order] ADD  CONSTRAINT [DF_Order_SubTime]  DEFAULT (getdate()) FOR [SubTime]
GO

  附上表结构和初始数据图:

  

一、ROW_NUMBER

  row_number的用途的非常广泛,排序最好用他,一般可以用来实现web程序的分页,他会为查询出来的每一行记录生成一个序号,依次排序且不会重复,注意使用row_number函数时必须要用over子句选择对某一列进行排序才能生成序号。row_number用法实例:

select ROW_NUMBER() OVER(order by [SubTime] desc) as row_num,* from [Order]

  查询结果如下图所示:

  

  图中的row_num列就是row_number函数生成的序号列,其基本原理是先使用over子句中的排序语句对记录进行排序,然后按照这个顺序生成序号。over子句中的order by子句与SQL语句中的order by子句没有任何关系,这两处的order by 可以完全不同,如以下sql,over子句中根据SubTime降序排列,Sql语句中则按TotalPrice降序排列。

select ROW_NUMBER() OVER(order by [SubTime] desc) as row_num,* from [Order] order by [TotalPrice] desc

  查询结果如下图所示:

  

  利用row_number可以实现web程序的分页,我们来查询指定范围的表数据。例:根据订单提交时间倒序排列获取第三至第五条数据。

with orderSection as
(
    select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order]
)
select * from [orderSection] where rownum between 3 and 5 order by [SubTime] desc

  查询结果如下图所示:

  

  注意:在使用row_number实现分页时需要特别注意一点,over子句中的order by 要与Sql排序记录中的order by 保持一致,否则得到的序号可能不是连续的。下面我们写一个例子来证实这一点,将上面Sql语句中的排序字段由SubTime改为TotalPrice。另外提一下,对于带有子查询和CTE的查询,子查询和CTE查询有序并不代表整个查询有序,除非显示指定了order by。

with orderSection as
(
    select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order]
)
select * from [orderSection] where rownum between 3 and 5 order by [TotalPrice] desc

  查询结果如下图所示:

  

  

二、RANK

  rank函数用于返回结果集的分区内每行的排名, 行的排名是相关行之前的排名数加一。简单来说rank函数就是对查询出来的记录进行排名,与row_number函数不同的是,rank函数考虑到了over子句中排序字段值相同的情况,如果使用rank函数来生成序号,over子句中排序字段值相同的序号是一样的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一,可以理解为根据当前的记录数生成序号,后面的记录依此类推。可能我描述的比较苍白,理解起来也比较吃力,我们直接上代码,rank函数的使用方法与row_number函数完全相同。

select RANK() OVER(order by [UserId]) as rank,* from [Order] 

  查询结果如下图所示:

  

  由上图可以看出,rank函数在进行排名时,同一组的序号是一样的,而后面的则是根据当前的记录数依次类推,图中第一、二条记录的用户Id相同,所以他们的序号是一样的,第三条记录的序号则是3。  

三、DENSE_RANK

  dense_rank函数的功能与rank函数类似,dense_rank函数在生成序号时是连续的,而rank函数生成的序号有可能不连续。dense_rank函数出现相同排名时,将不跳过相同排名号,rank值紧接上一次的rank值。在各个分组内,rank()是跳跃排序,有两个第一名时接下来就是第四名,dense_rank()是连续排序,有两个第一名时仍然跟着第二名。将上面的Sql语句改由dense_rank函数来实现。

select DENSE_RANK() OVER(order by [UserId]) as den_rank,* from [Order]

  查询结果如下图所示:

  

  图中第一、二条记录的用户Id相同,所以他们的序号是一样的,第三条记录的序号紧接上一个的序号,所以为2不为3,后面的依此类推。

四、NTILE

  ntile函数可以对序号进行分组处理,将有序分区中的行分发到指定数目的组中。 各个组有编号,编号从一开始。 对于每一个行,ntile 将返回此行所属的组的编号。这就相当于将查询出来的记录集放到指定长度的数组中,每一个数组元素存放一定数量的记录。ntile函数为每条记录生成的序号就是这条记录所有的数组元素的索引(从1开始)。也可以将每一个分配记录的数组元素称为“桶”。ntile函数有一个参数,用来指定桶数。下面的SQL语句使用ntile函数对Order表进行了装桶处理:

select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]

  查询结果如下图所示:

  

  Order表的总记录数是6条,而上面的Sql语句ntile函数指定的组数是4,那么Sql Server2005是怎么来决定每一组应该分多少条记录呢?这里我们就需要了解ntile函数的分组依据(约定)。

  ntile函数的分组依据(约定):

  1、每组的记录数不能大于它上一组的记录数,即编号小的桶放的记录数不能小于编号大的桶。也就是说,第1组中的记录数只能大于等于第2组及以后各组中的记录数。

  2、所有组中的记录数要么都相同,要么从某一个记录较少的组(命名为X)开始后面所有组的记录数都与该组(X组)的记录数相同。也就是说,如果有个组,前三组的记录数都是9,而第四组的记录数是8,那么第五组和第六组的记录数也必须是8。

  这里对约定2进行详细说明一下,以便于更好的理解。

  首先系统会去检查能不能对所有满足条件的记录进行平均分组,若能则直接平均分配就完成分组了;若不能,则会先分出一个组,这个组分多少条记录呢?就是 (总记录数/总组数)+1 条,之所以分配 (总记录数/总组数)+1 条是因为当不能进行平均分组时,总记录数%总组数肯定是有余的,又因为分组约定1,所以先分出去的组需要+1条。

  分完之后系统会继续去比较余下的记录数和未分配的组数能不能进行平均分配,若能,则平均分配余下的记录;若不能,则再分出去一组,这个组的记录数也是(总记录数/总组数)+1条。

  然后系统继续去比较余下的记录数和未分配的组数能不能进行平均分配,若能,则平均分配余下的记录;若还是不能,则再分配出去一组,继续比较余下的......这样一直进行下去,直至分组完成。

  举个例子,将51条记录分配成5组,51%5==1不能平均分配,则先分出去一组(51/5)+1=11条记录,然后比较余下的 51-11=40 条记录能否平均分配给未分配的4组,能平均分配,则剩下的4组,每组各40/4=10 条记录,分配完成,分配结果为:11,10,10,10,10,晓菜鸟我开始就错误的以为他会分配成 11,11,11,11,7。

  根据上面的两个约定,可以得出如下的算法:

//mod表示取余,div表示取整.
if(记录总数 mod 桶数==0)
{
  recordCount=记录总数 div 桶数;
  //将每桶的记录数都设为recordCount.
}
else
{
  recordCount1=记录总数 div 桶数+1;
  int n=1;//n表示桶中记录数为recordCount1的最大桶数.
  m=recordCount1*n;
  while(((记录总数-m) mod (桶数- n)) !=0)
  {
    n++;
    m=recordCount1*n;
  }
  recordCount2=(记录总数-m) div (桶数-n);
  //将前n个桶的记录数设为recordCount1.
  //将n+1个至后面所有桶的记录数设为recordCount2.
}

int recordTotal = 51;//记录总数.
int tcount = 5;//总组数.
string groupResult = "将" + recordTotal + "条记录分成" + tcount + "组,";
int recordCount = 0;//平均分配时每组的记录数.
//不能平均分配
int recordCount1 = 0;//前n个组每组的记录数.
int recordCount2 = 0;//第n+1组至后面所有组每个组的记录数.
int n = 1;//组中记录数为recordCount1的最大组数(前n组).
if (recordTotal % tcount == 0)//能平分.
{
    recordCount = recordTotal / tcount;//每组的记录数.
}
else//不能平分.
{
    recordCount1 = recordTotal / tcount + 1;//不能平分则先分出一组-前n组每组的记录数.
    int m = recordCount1 * n;//已分配的记录数.
    while ((recordTotal - m) % (tcount - n) != 0)//余下的记录数和未分配的组不能进行平分.
    {
        //还是不能平分,继续分出一组.
        n++;
        m = recordCount1 * n;
    }
    recordCount2 = (recordTotal - m) / (tcount - n);//余下的记录数和未分配的组能进行平分或者只剩下最后一组了-第n+1组至后面所有组每个组的记录数.
}
//输出.
if (recordCount != 0)
{
    groupResult += "能平均分配,每组" + recordCount + "个.";
}
else
{
    groupResult += "不能平均分配,前" + n + "组,每组" + recordCount1 + "个,";
    if (n < tcount - 1)
    {
        //groupResult += "第" + (groupNumber + 1) + "组至后面所有组,每组" + recordCount2 + "个.";
        groupResult += "第" + (n + 1) + "组至第" + tcount + "组,每组" + recordCount2 + "个.";
    }
    else
    {
        groupResult += "第" + (n + 1) + "组" + recordCount2 + "个.";
    }
}
ViewData["result"] = groupResult;

  

  根据上面的算法,如果总记录数为59,总组数为5,则 n=4 , recordCount1=12 , recordCount2=11,分组结果为 :12,12,12,12,11。

  如果总记录数为53,总组数为5,则 n=3 , recordCount1=11 , recordCount2=10,分组结果为:11,11,11,10,10。

  就拿上面的例子来说,总记录数为6,总组数为4,通过算法得到 n=2 , recordCount1=2 , recordCount2=1,分组结果为:2,2,1,1。

select ntile,COUNT([ID]) recordCount from
(
    select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]
) as t
group by t.ntile

  运行Sql,分组结果如图:

  

  比对算法与Sql Server的分组结果是一致的,说明算法没错。:)

总结:

在使用排名函数的时候需要注意以下三点:

  1、排名函数必须有 OVER 子句。

  2、排名函数必须有包含 ORDER BY 的 OVER 子句。

  3、分组内从1开始排序。

原文地址:https://www.cnblogs.com/Soprano/p/10659360.html

时间: 2024-10-11 14:27:58

排名函数的相关文章

SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较

排名函数是SQL Server2005新加的功能.在SQL Server2005中有如下四个排名函数: 1.row_number 2.rank 3.dense_rank 4.ntile 下面分别介绍一下这四个排名函数的功能及用法.在介绍之前假设有一个t_table表,表结构与表中的数据如图1所示: 图1 其中field1字段的类型是int,field2字段的类型是varchar 一.row_number row_number函数的用途是非常广泛,这个函数的功能是为查询出来的每一行记录生成一个序号

好用的排名函数~ROW_NUMBER(),RANK(),DENSE_RANK() 三兄弟

排名函数三兄弟,一看名字就知道,都是为了排名而生!但是各自有各自的特色!以下一个例子说明问题!(以下栗子没有使用Partition By 的关键字,整个结果集进行排序) RANK 每个值一个排名,同样的值排同样的位置,如第一名有2个,下一个值就要排第三,如此类推,表现如下面的 RandNr 列 DENSE_RANK 每个值一个排名,跟Rank 不一致的地方在于它不跳号,会1,2,3 那样排下来 ROW_NUMBER 每行一个排序值,遇到相同的排序条件的时候,按照顺序给值,对应表现如下面RowNr

SQL Server排名函数与排名开窗函数

什么是排名函数?说实话我也不甚清楚,我知道 order by 是排序用的,那么什么又是排名函数呢? 接下来看几个示例就明白了. 首先建立一个表,随便插入一些数据. ROW_NUMBER 函数:直接排序,ROW_NUMBER函数是以上升进行直接排序,并且以连续的顺序给每一行数据一个唯一的序号.(即排名连续) 1 -- 以下是根据 U_Pwd 这一列进行排名(升序) 2 select *, 3 '第'+convert(varchar,ROW_NUMBER() over(order by U_Pwd)

sql server 排名函数:DENSE_RANK

一.需求 之前sql server 的排名函数用得最多的应该是RoW_NUMBER()了,我通常用ROW_NUMBER() + CTE 来实现分页:今天逛园,看到另一个内置排名函数还不错,自己顺便想了一个需求,大家可以花1分钟先想想要怎么实现. 需求很简单:求成绩排名前五的学生信息. 例如: 由于成绩可以并列,所以前五名可能有多个.例如: 测试数据: declare @t table (ID int, StudentName nvarchar(15), Score int) insert int

SQL Server 排名函数

内容摘要: Transact-SQL提供了4个排名函数: RANK(),DENSE_RANK(),ROW_NUMBER(),NTILE(),下文是对这4个函数进行的解释. 下面是对这4个函数的解释: RANK() 返回结果集的分区内每行的排名.行的排名是相关行之前的排名数加一. 如果两个或多个行与一个排名关联,则每个关联行将得到相同的排名. 例如,如果两位销售员具有相同的SalesYTD值,则他们将并列第一.由于已有两行排名在前,所以具有下一个最大SalesYTD的销售人员将排名第三. 因此,R

你真的会玩SQL吗?表表达式,排名函数

这次讲的有些可能是经常用但不会注意到,所以来统一总结一下用法. 我们往往需要临时存储某些结果集.除了用临时表和表变量,还可以使用公用表表达式的方法. 表表达式 期待单个值的地方可以使用标量子查询 期待多个值的地方可以使用多值子查询 在期待出现表的地方可用表值子查询或表表达式 1.派生表 是从查询表达式派生出虚拟结果表的表表达式,派生表的存在范围只是外部查询. 使用形式:from 派生表 as 派生表列名 规则: 所有列必须有名称 列名必须唯一 不允许使用order by(除非指定了top) 不同

TSQL入门(二) - 排名函数

over() 开窗函数 排名函数必须和over()一起使用,所以先了解over(). OVER 子句定义查询结果集内的窗口或用户指定的行集. 然后,开窗函数将计算窗口中每一行的值. 您可以将 OVER 子句与函数一起使用,以便计算各种聚合值,例如移动平均值.累积聚合.运行总计或每组结果的前 N 个结果. select *,COUNT(*) over() '全班人数' from Student select *,COUNT(*) over(partition by Ssex) '性别人数' fro

四个排名函数(row_number、rank、dense_rank和ntile)的比较

排名函数是SQL Server2005新加的功能.在SQL Server2005中有如下四个排名函数: 1.row_number 2.rank 3.dense_rank 4.ntile 下面分别介绍一下这四个排名函数的功能及用法.在介绍之前假设有一个t_table表,表结构与表中的数据如图1所示: 图1 其中field1字段的类型是int,field2字段的类型是varchar 一.row_number row_number函数的用途是非常广泛,这个函数的功能是为查询出来的每一行记录生成一个序号

SQL Server 排名函数实现

在SQL Server 中有四大排名函数分别是: 1.row_number() 2.ntile() 3.rank() 4.dense_rank() ------------------------------------------------------------------------- 为了方便演示我们先建立相关的表 create table t(ID int ,Name varchar(32));go insert into t(ID,Name) values(1,'A'),(2,'A

SQLServer学习笔记&lt;&gt;.基础知识,一些基本命令,单表查询(null top用法,with ties附加属性,over开窗函数),排名函数

Sqlserver基础知识 (1)创建数据库 创建数据库有两种方式,手动创建和编写sql脚本创建,在这里我采用脚本的方式创建一个名称为TSQLFundamentals2008的数据库.脚本如下:   同时往数据库表插入一些数据,用户后续对数据库的sql的练习.在这里有需要的可以下载相应的脚本进行数据库的初始化.我放到百度云上面,请戳 我:http://yun.baidu.com/share/link?shareid=3635107613&uk=2971209779,提供了<Sqlserver