最近使用窗口函数的频率越来越高,这里打算简单介绍一下几个排序的函数,做一个引子希望以后这方面的问题能够更深入的理解,这里先简单介绍一下几个简单的排序函数及其相关子句,这里先从什么是排序开始吧。

排序函数是做什么的?

排序函数的作用是基于一个结果集返回一个排序值。排序值就是一个数字,这个数字是典型的以1开始且自增长为1的行值。由ranking函数决定排序值可以使唯一的对于当前结果集,或者某些行数据有相同的排序值。在接下来我将研究不同的排序函数以及如何使用这些函数。

使用RANK函数的例子

RANK函数每个分区的排序都是从1开始。“partition”是一组有相同指定分区列值的数据行的集合。如果一个分区中有相同排序列的值(这个列指定在ORDER BY后面),然后相同排序列值的行将会分配给相同的排序值。有点绕口,为了更好的理解,如何使用,让我们看下下面的语法:

RANK ( ) OVER ( [ PARTITION BY <partition_column> ] ORDER BY <order_by_column> )

这里有几个参数:

  • <partition_column>: 指定一个或者多个列名作为分区数据
  • <order by column>: 确定一个或者多个列然后用来对每个分区的输出数据进行排序
注意:

PARTITION BY子句是一个可选项。如是不使用,数据将按照一个分区对所有数据进行排序。如果指定了PARTITION BY子句,则每个分区的数据集都各自进行从1开始的排序。

现在对RANK函数的语法和如何工作有了一定的理解,下面运行一对该函数的例子。需要说明一下我的例子的运行环境都是AdventureWorks2012 数据库,可以从网络上下载这里给出一个下载地址http://msftdbprodsamples.codeplex.com/releases/view/93587

下面是第一个使用RANK函数的例子:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
RANK() OVER
(ORDER BY PostalCode ASC) AS RankingValue
FROM Person.Address
WHERE StateProvinceID IN (23,46);

Code1: 只有RANK函数不分区

运行代码后,结果集如下:

PostalCode      StateProvinceID RankingValue
--------------- --------------- --------------------
03064 46 1
03064 46 1
03106 46 3
03276 46 4
03865 46 5
83301 23 6
83402 23 7
83501 23 8
83702 23 9
83864 23 10
 

如上所示,按照RANK函数使结果集按照列RankingValue进行了排序。在例子中排序是基于列PostalCode。每一个唯一的PostalCode 得到一个不同的排序值。这里PostalCode 为03054 有两行数据,它们的排序值都是1,因为有两个1,所以排序2就被跳过。其余的排序继续往下依次进行。

由于RANK函数的分区子句没有使用,那么整个结果集被当做一个单一的分区。如果我打算按照独立的StateProvinceID 进行分区,然后进行排序我可以做按照如下的例子来执行:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
RANK() OVER
(PARTITION BY StateProvinceID
ORDER BY PostalCode ASC) AS RankingValue
FROM Person.Address
WHERE StateProvinceID IN (23,46);

Code 2: 使用分区子句

运行代码后的结果集:

PostalCode      StateProvinceID RankingValue
--------------- --------------- --------------------
83301 23 1
83402 23 2
83501 23 3
83702 23 4
83864 23 5
03064 46 1
03064 46 1
03106 46 3
03276 46 4
03865 46 5

在输出结果中分为了两个分区,一个分区是StateProvinceID 是23的,而另一个是包含StateProvinceID 值为46的、注意每个分区都是从1开始进行排序的。

使用DENSE_RANK函数

当运行RANK函数时,由于有一个相同的PostalCode ,输出结果会跳过一个排序值2,通过使用DENSE_RANK函数我能生成一个不省略改相同排序值的一个排序。该函数语法如下:

DENSE_RANK ( ) OVER ( [ PARTIION BY <partition_column> ] ORDER BY <order_by_column> )

语法中唯一的不同就是函数名称的改变。让我们运行下面的代码来研究下函数:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
DENSE_RANK() OVER
(PARTITION BY StateProvinceID
ORDER BY PostalCode ASC) AS RankingValue
FROM Person.Address
WHERE StateProvinceID IN (23,46);

Code3: 使用 DENSE_RANK

结果集如下:

PostalCode      StateProvinceID RankingValue
--------------- --------------- --------------------
83301 23 1
83402 23 2
83501 23 3
83702 23 4
83864 23 5
03064 46 1
03064 46 1
03106 46 2
03276 46 3
03865 46 4

根据结果集,可以看到PostalCode 03064 有相同的排序值,但是下一个PostalCode 的排序值为2而不是3了。与RANK函数的不同就是当有重复排序值时它能保证了排序序列中没有省略排序。

使用NTILE 函数

该函数将数据集合划分为不同的组。得到组的数量是根据指定的一个整数来确定的。下面就是NTILE 函数的语法:

NTILE (integer_expression) OVER ( [ PARTIION BY <partition_column> ] ORDER BY <order_by_column> )

Where:

  • <integer_expression>: 确定创建不同组的数量
  • <partition_column>:确定一个或者多个列用来进行分区数据
  • <order by column>: 确定一个或者多个列然后用来对每个分区的输出数据进行排序

为了更好地理解,让我们回顾几个不同的例子。运行下面代码:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
NTILE(2) OVER
(ORDER BY PostalCode ASC) AS NTileValue
FROM Person.Address
WHERE StateProvinceID IN (23,46);

Code4: 使用NTILE 函数查询

运行结果如下:

PostalCode      StateProvinceID NTileValue
--------------- --------------- --------------------
03064 46 1
03064 46 1
03106 46 1
03276 46 1
03865 46 1
83301 23 2
83402 23 2
83501 23 2
83702 23 2
83864 23 2

通过观察结果集,能很容易发现有两个不同的NTileValue 的列值,1和2。两个不同的NTileValue 值被创建是因为这里我查询语句中指定了“NTILE(2)” 。这个括号内的值就是整数表达式,作用就是指定创建的组的数量。当看到结果集中有10行数据,前五行NTileValue 为1,后五行为2。不出所料整个结果集被平均分成了两组。

如果不能被平均分配到不同个组的时候,比如参数导致有不能被整除的时候。当发生这种情况是那么将不能被整除的行按序放到每一个组内,知道所有的剩余行都被分配完毕。如下所示:

USE AdventureWorks2012;
GO
DECLARE @Integer_Expression int = 4;
SELECT PostalCode, StateProvinceID,
NTILE(@Integer_Expression) OVER
(ORDER BY PostalCode ASC) AS NTileValue
FROM Person.Address
WHERE StateProvinceID IN (46,23);

Code 5: NTile 查询不能平均分配结果集

运行代码如下:

PostalCode      StateProvinceID NTileValue
--------------- --------------- --------------------
03064 46 1
03064 46 1
03106 46 1
03276 46 2
03865 46 2
83301 23 2
83402 23 3
83501 23 3
83702 23 4
83864 23 4

这里直奔主题,10个结果行,参数为4需要分成4组,那么10除以4 余数为2。这意味着前两组会多一行比后两组。如上所示,在这个输出结果中1和2组都有3行,然后NTileValue 为3和4的组只有两行。

跟RANK函数一样,我们也能使用partition 分区子句来创建分区下的NTILE 函数。当引入PARTITION BY 子句时,每个分区内部都从1开始进行NTILE排序。下面展示一下运行代码:

USE AdventureWorks2012;
GO
DECLARE @Integer_Expression int = 3;
SELECT PostalCode, StateProvinceID,
NTILE(@Integer_Expression) OVER
(PARTITION BY StateProvinceID
ORDER BY PostalCode ASC) AS NTileValue
FROM Person.Address
WHERE StateProvinceID IN (46,23);

Code 6: 使用分区子句后,使用NTile 查询不平均分组

运行代码如下:

PostalCode      StateProvinceID NTileValue
--------------- --------------- --------------------
83301 23 1
83402 23 1
83501 23 2
83702 23 2
83864 23 3
03064 46 1
03064 46 1
03106 46 2
03276 46 2
03865 46 3

通过结果集可以看到加入分区子句后对NTILE函数的影响。如果观察输出的NTileValue列值,可以发现排序从StateProvinceID  为46开始重新从1开始。这就是加入“PARTITION BY StateProvinceID”子句的作用,先分区在分组排序。

使用 ROW_NUMBER 函数

当打算为输出的行生成一个行号时,行号顺序地自增长,步长为1.为了完成目标我们需要使用ROW_NUMBER 函数。

下面是使用ROW_NUMBER 的例子:

ROW_NUMBER () OVER ( [ PARTIION BY <partition_expressions> ] ORDER BY <order_by_column> )

代码如下:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
ROW_NUMBER() OVER
(ORDER BY PostalCode ASC) AS RowNumber
FROM Person.Address
WHERE StateProvinceID IN (46,23);

Code  7: 使用ROW_NUMBER 函数

运行结果如下:

PostalCode      StateProvinceID RowNumber
--------------- --------------- --------------------
03064 46 1
03064 46 2
03106 46 3
03276 46 4
03865 46 5
83301 23 6
83402 23 7
83501 23 8
83702 23 9
83864 23 10

如果想对输出的PostalCode进行排序,但是你打算先按照StateProvinceID进行分组,再排序。为了实现上述要求,我加入PARTITION BY子句,代码如下:

USE AdventureWorks2012;
GO
SELECT PostalCode, StateProvinceID,
ROW_NUMBER() OVER
(PARTITION BY StateProvinceID
ORDER BY PostalCode ASC) AS RowNumber
FROM Person.Address
WHERE StateProvinceID IN (46,23);

Code 8: 使用PARTITION BY 子句和ROW_NUMBER 函数查询

运行结果如下:

PostalCode      StateProvinceID RowNumber
--------------- --------------- --------------------
83301 23 1
83402 23 2
83501 23 3
83702 23 4
83864 23 5
03064 46 1
03064 46 2
03106 46 3
03276 46 4

正如你看到的结果,通过添加分区子句,行数列RowNumber 每个不同的StateProvinceID 值都会从1重新开始排序。

总结

本篇讲了多种不同的排序数据的方式,并且有一些方式要求分配一个序列化的数字。我先后展示了如何使用ROW_NUMBER, NTILE, RANK 和 DENSE_RANK函数,如何为每一行数据生成序列化的列值。希望能够让大家在使用时更方便,这里也只是展示了一部分窗口函数的使用。还有很多新的窗口函数希望跟大家一起讨论学习。这里只是做一个简单介绍了。

SQL中几个常用的排序函数的更多相关文章

  1. sql 中实现打乱数据的排序

    sql 中实现打乱数据的排序    order by NEWID()就实现了数据的打乱 

  2. SQL 中详解round(),floor(),ceiling()函数的用法和区别?

    SQL 中详解round(),floor(),ceiling()函数的用法和区别? 原创 2013年06月09日 14:00:21   摘自:http://blog.csdn.net/yueliang ...

  3. Sql 中获取年月日时分秒的函数

    getdate():获取系统当前时间 dateadd(datepart,number,date):计算在一个时间的基础上增加一个时间后的新时间值,比如:dateadd(yy,30,getdate()) ...

  4. 处理PHP中字符串的常用操作及函数

    1. 确定一个字符串的长度 这是最为常见和基础的例子,对于确定一个字符串的长度,我们应该使用strlen()函数,比如要获取下面字符串$text 的长度: $text = "sunny da ...

  5. C语言中几个常用数学计算函数ceil(), floor(), round()的用法

    最近在实现算法的过程中,遇到了使用几个数学计算函数,感觉挺有意思,就记下来 方便以后使用. ceil(x)返回不小于x的最小整数值(然后转换为double型). floor(x)返回不大于x的最大整数 ...

  6. sql中详解round(),floor(),ceiling()函数的用法和区别?

    round() 遵循四舍五入把原值转化为指定小数位数,如:round(1.45,0) = 1;round(1.55,0)=2floor()向下舍入为指定小数位数 如:floor(1.45,0)= 1; ...

  7. SQL中的join操作总结(非常好)

    1.1.1 摘要 Join是关系型数据库系统的重要操作之一,SQL Server中包含的常用Join:内联接.外联接和交叉联接等.如果我们想在两个或以上的表获取其中从一个表中的行与另一个表中的行匹配的 ...

  8. 在SQL中使用CLR提供基本函数对二进制数据进行解析与构造

      二进制数据包的解析一般是借助C#等语言,在通讯程序中解析后形成字段,再统一单笔或者批量(表类型参数)提交至数据库,在通讯程序中,存在BINARY到struct再到table的转换. 现借助CLR提 ...

  9. C++ 排序函数 sort(),qsort()的使用方法

    想起来自己天天排序排序,冒泡啊,二分查找啊,结果在STL中就自带了排序函数sort,qsort,总算把自己解脱了~ 所以自己总结了一下,首先看sort函数见下表: 函数名 功能描写叙述 sort 对给 ...

随机推荐

  1. ASP.NET MVC Model验证(三)

    ASP.NET MVC Model验证(三) 前言 上篇中说到在MVC框架中默认的Model验证是在哪里验证的,还讲到DefaultModelBinder类型的内部执行的示意图,让大家可以看到默认的M ...

  2. 海量数据处理利器greenplum——初识

    简介及适用场景 如果想在数据仓库中快速查询结果,可以使用greenplum. Greenplum数据库也简称GPDB.它拥有丰富的特性: 第一,完善的标准支持:GPDB完全支持ANSI SQL 200 ...

  3. 修改Coney主题之侧边栏移位

    title: 修改Coney主题之侧边栏移位 date: 2014-12-15 18:09:54 categories: Hexo tags: [hexo,css] --- Coney是一个非常漂亮的 ...

  4. 打造android偷懒神器———RecyclerView的万能适配器

    转载请注明出处谢谢:http://www.cnblogs.com/liushilin/p/5720926.html 很不好意思让大家久等了,本来昨天就应该写这个的,无奈公司昨天任务比较紧,所以没能按时 ...

  5. 「译」JUnit 5 系列:架构体系

    原文地址:http://blog.codefx.org/design/architecture/junit-5-architecture/ 原文日期:29, Mar, 2016 译文首发:Linesh ...

  6. js正则表达式校验非负整数:^\d+$ 或 ^[1-9]\d*|0$

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. 通过向页面写html代码导出excel

    //excel文件名 string filename = "考勤汇总"; StringBuilder ExcelHtml = new StringBuilder(); ExcelH ...

  8. 创建虚拟目录失败,必须为服务器名称指定“localhost”?看进来!!

    没废话,直接讲! 关于微信开发过程,远程调试后,再次打开vs出现项目加载失败的解决办法! 上图: 这图应该不陌生,你肯定打开iis把绑定的域名给干掉了.这个提示很坑人,简直就是坑爹!!!fck!! 来 ...

  9. 继续上篇抢QQ口令红包,抢那招抢不了的红包技巧

    - - - - - - - - - - -- - - --长按红包,出现回复,点击回复,那回复里有个表情,直接输入那个表情回复就可以抢了 - - - - - - - - --------------- ...

  10. .NET Core 2.0版本预计于2017年春季发布

    英文原文: NET Core 2.0 Planned for Spring 2017 微软项目经理 Immo Landwerth 公布了即将推出的 .NET Core 2.0 版本的细节,该版本预计于 ...