SQL点滴20—T-SQL中的排名函数

简介:   提到排名函数我们首先可能想到的是order by,这个是排序,不是排名,排名需要在前面加个名次序号的,order by是没有这个功能的。还可能会想到identity(1,1),它也给了一个序号,但是不能保证给出的序号是连续升序的。

 

提到排名函数我们首先可能想到的是order by,这个是排序,不是排名,排名需要在前面加个名次序号的,order by是没有这个功能的。还可能会想到identity(1,1),它也给了一个序号,但是不能保证给出的序号是连续升序的。除非能够保证所有的Insert语句都能够正确成功地完成,并且没有删除操作,实际的使用中大多数的表都不能保证这样。

好在SQL Server中提供了一些排名函数来辅助实现这些功能。排名函数按照需要的顺序对数据进行排名,并提供一个值对数据。下面来了解一下这些排序函数功能。

  

 

ROW_NUMBER

 

ROW_NUMBER函数允许以上升,连续的顺序给每一行数据一个序号,注意ROW_NUMBER()后面一定要跟着over子句。来看语句:

 
 
1 use AdventureWorks
2 select
3 ROW_NUMBER() over ( order by LastName) as RowNum,
4 FirstName + ' ' + LastName as FullName
5 from HumanResources.vEmployee
6 where JobTitle = ' Production Technician - WC60 '

这个语句对符合条件(JobTitle='Production Technician - WC60')的LastName按照升序排列,并加上排序的序号,这个序号是连续上升的。结果如下图1是部分结果。

img_865125dd10147bc5d995f1f62bffa15d.png

图1

我们可以看到第一个人的LastName是Abercrombie,第二个人的LastName是Adams,以次类推。

  

 

PARTITION

 

如果我们想再细分一下,在一个小的分组范围内排序该怎么办呢?就是说让LastName以‘A’开头的作为第一组,在这个组内进行排序。以‘B’开头的作为第二组,在这个组内排序。以‘C’开头的作为第三组,在这个组内进行排序,如此等等。这里有一个很简单的实际例子,假如上面这些人都来参加同一场马拉松比赛,其中有男子组,女子组,男子残疾组,女子残疾组,60岁以上组等等。不管参赛者以第几位触线,名次都以他们的小组为基准。

可以通过PARTITION BY选项来重新排序,给数据分区或者数据区域唯一的递增序号。来看下面的语句:

[注] partition n. 划分,分开;[数] 分割;隔墙;隔离物;vt. [数] 分割;分隔;区分

 
 
1 select
2 ROW_NUMBER() over (PARTITION by substring (LastName, 1 , 1 ) order by LastName) as RowNum,
3 FirstName + ' ' + LastName as FullName
4 from HumanResources.vEmployee
5 where JobTitle = ' Production Technician - WC60 '

这里模拟上面的情况,首先以Last Name的第一个字母作为分组,然后以第二个字母以后的字母来分组排序。来看看结果,如图2

img_ebef240a8ae74015ed27f003014500e2.png

图2

 

假设LastName以‘A’开头的是男子组,这个组有共有三个人,Kim Abercrombie是冠军,Jay Adams是亚军,Nancy Anderson是季军。假设LastName以‘B’开头的是女子组,这个组只有一个人Bryan Baker,无论如何她都是冠军。等等如此类推。这样一眼就能看出他们的小组名次了。

这里你可能会觉得使用order by一样可以得到这样类似的结果。如下代码:

 
 
1 select
2 FirstName+' '+ LastName as FullName
3 from HumanResources.vEmployee
4 where JobTitle='Production Technician - WC60'
5 order by substring(LastName,1,1) ,LastName

这个把order by放在最后,排序放在最后,首先按照LastName的首字母排序,再按照剩整个LastName排序,结果如下图3

img_eb3fae105215bebf2883184d5541c685.png

图3

结果和上面大致相同,可是少了前面的名次序号。于是我又对她进行了修改,代码如下:

 
 
1 select
2 ROW_NUMBER() over ( order by substring (LastName, 1 , 1 ),LastName) as RowNum,
3 FirstName + ' ' + LastName as FullName
4 from HumanResources.vEmployee
5 where JobTitle = ' Production Technician - WC60 '
这个和上面的类似,在排名函数中使用order by,并且是按照多个字段排序。来看看结果如图4
 

img_7205c3303368949e23263efd09228416.png

图4

排序没有错误,是我们想要的分组排序,但是前面的名次没有分组区分,和图1没有什么差别。可见图3和图4的做法完全是多余,纯属臆造,其实只要order by LastName都能得到正确的排序,只有partition by才是正解。通过上面的例子也可以对排序,排名这二者之间的区别有一个认识,他们虽然有相似之处,但是排名始终会产生一个名次序号,排序只要得到正确的顺序就好。

     

RANK

 

还是拿马拉松比赛来说事,如果有同时撞线的情况发生应该怎么计名次呢?例如A第一个撞线,B和C同时第二个撞线,D第三个撞线,如果我们想把D的名次计为第4名应该怎么处理呢?就是说不计顺序名次,只计人数。这时就可以使用RANK函数了。

[注] rank n. 等级;队列;排;军衔vt. 排列;把…分等vi. 列队;列为

在order by子句中定义的列上,如果返回一行数据与另一行具有相同的值,rank函数将给这些行赋予相同的排名数值。在排名的过程中,保持一个内部计数值,当值有所改变时,排名序号将有一个跳跃。

来看下面的语句:

 
 
1 select
2 ROW_NUMBER() over ( order by Department) as RowNum,
3 RANK() over ( order by Department) as Ranking,
4 FirstName + ' ' + LastName as FullName,
5 Department
6 from HumanResources.vEmployeeDepartment
7 order by RowNum

rank()函数右面也要跟上一个over子句。为了看到效果我们以Department作为排序字段,可以看到RowNum作为升序连续排名,Ranking作为计同排名,当Department的值相同时,Ranking中的值保持不变,当Ranking中的值发生变化时,Ranking列中的值将跳跃到正确的排名数值。来看结果:

img_52f03253c8b9db5493d7bf6395241ffd.png

图5

从这个结果中我们可以说这次马拉松赛跑的排名是:Tengiz Kharatishvili,Zainal Arifin,Sean Chai,Karen Berge,Chris Norred并列第1,Michael Sullivan,Sharon Salavaria,Roberto Tamburello,Gail Erickson,Jossef Goldberg并列第6,如此等等。



 

DENSE_RANK

 

在上面的例子中,A第一个撞线,B和C同时第二个撞线,D第三个撞线,如果我们想把B和C的名次计位第2名,D的名次计为第3名应该怎么处理呢?就是说考虑并列名次。这里使用DENSE_RANK函数,来看下面的代码。 

 

 
  
1 select
2 ROW_NUMBER() over ( order by Department) as RowNum,
3 DENSE_RANK() over ( order by Department) as Ranking,
4 FirstName + ' ' + LastName as FullName,
5 Department
6 from HumanResources.vEmployeeDepartment
7 order by RowNum

 结果如下:

img_d128e5a98cb31a4fe14c76d1997802d5.png

图6

 

按照这个结果,我们可以说这次马拉松赛跑的排名是:Tengiz KharatishviliZainal ArifinSean ChaiKaren BergeChris Norred并列第1Michael SullivanSharon SalavariaRoberto TamburelloGail EricksonJossef GoldbergTerri Duffy并列第2,等等如此。

 

NTILE

 

在开始这个之前,先来一段小插曲。梭罗是铅笔的发明者,不过他没有申请专利。据说他天赋异禀,在父亲的铅笔厂里面打包铅笔的时候,从一堆铅笔里面抓取一把,每次都能精确地抓到一打12支。他在森林中目测两颗树之间的距离,和护林员用卷尺测量的结果相差无几。现在如果我们想从一张表中抓取多比数据,每一笔都是相同的数目,并且标明第几组该怎么办呢?NTILE函数提供了这个功能,他能。来看代码:

 
  
1 select
2 NTILE( 30 ) over ( order by Department) as NTiles,
3 FirstName + ' ' + LastName as FullName,
4 Department
5 from HumanResources.vEmployeeDepartment

现在我们要抓取30个组的数据,并保证尽可能的保证每组数目相同。结果如下,

img_b47f6878ace92211296bfe277139ab38.png

图7

这个视图中共290条数据,290/30=9.7约等于10,所以每组10条数据,如图每一条数据都有一个组号。这个结果要比索罗精确。

作者:Tyler Ning
出处:http://www.cnblogs.com/tylerdonet/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com  联系我,非常感谢。

目录
相关文章
|
2月前
|
SQL Oracle 关系型数据库
SQL优化-使用联合索引和函数索引
在一次例行巡检中,发现一条使用 `to_char` 函数将日期转换为字符串的 SQL 语句 CPU 利用率很高。为了优化该语句,首先分析了 where 条件中各列的选择性,并创建了不同类型的索引,包括普通索引、函数索引和虚拟列索引。通过对比不同索引的执行计划,最终确定了使用复合索引(包含函数表达式)能够显著降低查询成本,提高执行效率。
|
2月前
|
SQL 数据库 数据库管理
数据库SQL函数应用技巧与方法
在数据库管理中,SQL函数是处理和分析数据的强大工具
|
2月前
|
SQL 数据库 索引
SQL中COUNT函数结合条件使用的技巧与方法
在SQL查询中,COUNT函数是一个非常常用的聚合函数,用于计算表中满足特定条件的记录数
|
2月前
|
SQL 关系型数据库 MySQL
SQL日期函数
SQL日期函数
|
4月前
|
SQL 数据库
|
4月前
|
SQL 数据采集 数据处理
如何在 SQL Server 中使用 LEN 函数
【8月更文挑战第9天】
178 1
如何在 SQL Server 中使用 LEN 函数
|
3月前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
4月前
|
SQL 数据处理 数据库
SQL中的函数有哪些类型
【8月更文挑战第20天】SQL中的函数有哪些类型
47 1
|
4月前
|
SQL 监控 索引
如何在 SQL Server 中使用 `PATINDEX` 函数
【8月更文挑战第8天】
444 9
|
4月前
|
SQL 数据处理 数据库