Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介

 

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

  1. CREATE TABLE [dbo].[Order](
  2. [ID] [int] IDENTITY(1,1) NOT NULL,
  3. [UserId] [int] NOT NULL,
  4. [TotalPrice] [int] NOT NULL,
  5. [SubTime] [datetime] NOT NULL,
  6. CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED
  7. (
  8. [ID] ASC
  9. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  10. ) ON [PRIMARY]
  11.  
  12. GO
  13. SET IDENTITY_INSERT [dbo].[Order] ON
  14.  
  15. GO
  16. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (1, 1, 100, CAST(0x0000A419011D32AF AS DateTime))
  17. GO
  18. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (2, 2, 500, CAST(0x0000A419011D40BA AS DateTime))
  19. GO
  20. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (3, 3, 300, CAST(0x0000A419011D4641 AS DateTime))
  21. GO
  22. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (4, 2, 1000, CAST(0x0000A419011D4B72 AS DateTime))
  23. GO
  24. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (5, 1, 520, CAST(0x0000A419011D50F3 AS DateTime))
  25. GO
  26. INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (6, 2, 2000, CAST(0x0000A419011E50C9 AS DateTime))
  27. GO
  28. SET IDENTITY_INSERT [dbo].[Order] OFF
  29. GO
  30. ALTER TABLE [dbo].[Order] ADD CONSTRAINT [DF_Order_SubTime] DEFAULT (getdate()) FOR [SubTime]
  31. GO

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

  

一、ROW_NUMBER

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

  1. 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降序排列。

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

  查询结果如下图所示:

  

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

  1. with orderSection as
  2. (
  3. select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order]
  4. )
  5. 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。

  1. with orderSection as
  2. (
  3. select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order]
  4. )
  5. 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函数完全相同。

  1. 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函数来实现。

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

  查询结果如下图所示:

  

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

四、NTILE

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

  1. 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。

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

  1. //mod表示取余,div表示取整.
  2. if(记录总数 mod 桶数==0)
  3. {
  4.   recordCount=记录总数 div 桶数;
  5.   //将每桶的记录数都设为recordCount.
  6. }
  7. else
  8. {
  9.   recordCount1=记录总数 div 桶数+1;
  10.   int n=1;//n表示桶中记录数为recordCount1的最大桶数.
  11.   m=recordCount1*n;
  12.   while(((记录总数-m) mod (桶数- n)) !=0)
  13.   {
  14.     n++;
  15.     m=recordCount1*n;
  16.   }
  17.   recordCount2=(记录总数-m) div (桶数-n);
  18.   //将前n个桶的记录数设为recordCount1.
  19.   //将n+1个至后面所有桶的记录数设为recordCount2.
  20. }
 NTILE()函数算法实现代码

  

  根据上面的算法,如果总记录数为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。

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

  运行Sql,分组结果如图:

  

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

总结:

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

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

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

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

感谢:

  在博文的最后我要感谢园友 海岸线,他写的 SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较 对我帮助很大,非常感谢!

感谢您怀着耐心看完整篇博文!!!
如果文章有什么错误或不当之处,请您斧正!
您有任何意见或者建议,您可以给我发邮件,也可以在下面留言,我看到了会第一时间回复您的,谢谢!
作者:晓菜鸟
出处:http://www.cnblogs.com/52XF/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

SQL With As 用法Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介的更多相关文章

  1. SQL Server中排名函数row_number,rank,dense_rank,ntile详解

    SQL Server中排名函数row_number,rank,dense_rank,ntile详解 从SQL SERVER2005开始,SQL SERVER新增了四个排名函数,分别如下:1.row_n ...

  2. SQL Server:排名函数row_number,rank,dense_rank,ntile详解

    1.Row_Number函数 row_number函数大家比较熟悉一些,因为它的用途非常的广泛,我们经常在分页与排序中用到它,它的功能就是在每一行中生成一个连续的不重复的序号 例如: select S ...

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

    排名函数三兄弟,一看名字就知道,都是为了排名而生!但是各自有各自的特色!以下一个例子说明问题!(以下栗子没有使用Partition By 的关键字,整个结果集进行排序) RANK 每个值一个排名,同样 ...

  4. Spark2 Dataset分析函数--排名函数row_number,rank,dense_rank,percent_rank

    select gender,       age,       row_number() over(partition by gender order by age) as rowNumber,    ...

  5. 知方可补不足~row_number,rank,dense_rank,ntile排名函数的用法

    回到目录 这篇文章介绍SQL中4个很有意思的函数,我称它的行标函数,它们是row_number,rank,dense_rank和ntile,下面分别进行介绍. 一 row_number:它为数据表加一 ...

  6. SQL Server - 四种排序, ROW_NUMBER() /RANK() /DENSE_RANK() /ntile() over()

    >>>>英文版 (更简洁易懂)<<<< 转载自:https://dzone.com/articles/difference-between-rownum ...

  7. 开窗函数_ROW_NUMBER() / RANK() / DENSE_RANK() / NTILE() ------4个排名函数训练_1

    排名函数(训练,其实从SQL2005时就已经被引入) /*SQL Server 2012从零开始学_7.8  排序函数*/ --DROP TABLE fruits GO Create table fr ...

  8. SQL-OVER与四种排名函数:ROW_NUMBER(),RANK(),DENSE_RANK(),NTILE()

    1 SELECT orderid,custid,val, ROW_NUMBER() OVER(ORDER BY val) AS rownum, RANK() OVER(ORDER BY val) AS ...

  9. sqlserver 中row_number,rank,dense_rank,ntile排名函数的用法

    1.row_number() 就是行号 2.rank:类似于row_number,不同之处在于,它会对order by 的字段进行处理,如果这个字段值相同,那么,行号保持不变 3.dense_rank ...

  10. PCB MS SQL 排序应用(row_number rank dense_rank NTILE PARTITION)

    一.排序前,准备数据 --表变量 ),流程数 int) insert into @table union all union all union all union all --查看一下 select ...

随机推荐

  1. Photoshop 手动画金标准流程

    以下给出Photoshop手动画金标准的流程, 1. 读取 图片 2. 找到套锁button 3. 利用套锁button手动画金标准 4. 点击套锁区域.右键新建图层 此时能够看到右側出现新建的图层1 ...

  2. android实现跑马灯效果(能够实现两个以上跑马灯)

    本文用了继承自TextView的MarqueeTextView来实现跑马灯效果.原因是,跑马灯效果是须要TextView拥有焦点才会跑动的.而有时候TextView获得焦点会有点耗时,造成要等待一段时 ...

  3. Codeforces Round #311 (Div. 2)A Ilya and Diplomas

    [比赛链接]click here~~ [题目大意] n个人,获取一到三等文凭,每门文凭有大小范围.求最后文凭各颁发多少 [解题思路]直接枚举了, 看完题,赶紧写了一发代码,发现竟然错过注冊时间.系统提 ...

  4. HDU4336:Card Collector

    题意 有n张卡片,每一次 有pi的概率买到第i张卡.求买到所有卡的期望购买次数. n<=20 解析 Solution 1:大力状压(就是步数除以方案数) #include<iostream ...

  5. la3713

    2-sat...求解2-sat方案直接每个变量枚举就行了,lrj的代码很靠谱... #include<bits/stdc++.h> using namespace std; ; struc ...

  6. ubuntu16.04 Kafka 安装

    Kafka核心概念: 下面介绍Kafka相关概念,以便运行下面实例的同时,更好地理解Kafka. 1. Broker Kafka集群包含一个或多个服务器,这种服务器被称为broker 2. Topic ...

  7. E20170627-hm

    confirmation   n. 证实; 证明; 确认,

  8. poj Code(组合数)

    Code Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 9918   Accepted: 4749 Description ...

  9. node.js的模块引用

    1.模块的引用示例 var      math   =    require(‘math’): 在common.js规范中,存在require()方法,这个方法接受模块标识,此引引入一个模块的api ...

  10. C#使用Parallel处理数据同步写入Datatable并使用BulkInsert批量导入数据库

    项目需要,几十万张照片需要计算出每个照片的特征值(调用C++编写的DLL). 业务流程:选择照片文件夹,分别访问照片-->调用DLL接口传递照片路径-->接收处理返回值-->写入数据 ...