此文为翻译,由于本人水平有限,疏漏在所难免,欢迎探讨指正。

原文链接:传送门

使用NTILE函数的示例

NTILE函数将一组记录分割为几个组。其返回的分组数是由一个整形表达式指定的。如下你会找到NTILE函数的句法格式:

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

其中:

  • <integer_expression>:指定了将要创建的不同分组的数目
  • <partition_column>:指定了一个或者多个列名,其将用来对数据进行分区
  • <order by column>: 指定了一个或者多个列名,其将用来对各个分区的输出进行排序

为了更好的理解NTILE函数是做什么的,让我们来查看一些不同的例子。

对于第一个例子,我们假设你想将每一个PostalCode归为两个分组中的一个。为了满足这个需求,我将运行在列表4的代码中的NTILE函数:

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

列表4:NTILE查询

当我运行列表4的代码,我将得到结果4的结果:

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

结果4:从运行列表4的代码产生的输出

通过查看结果4的输出,你可以看到会有两个不同的NTileValue列值,1和2。之所以有两个不同的NTileValue值被创建,是因为我在列表4的查询语句中指定了“NTILE(2)”。在紧接着NTILE函数名的括号中的值是一个整型表达式,其指定了应该被创建的分组数。如同你在结果4中看到的,有10行数据被返回。前5行具有一个NTileValue 1,后5行具有NTileValue 2。列表4中的代码按预期创建了数据行的两个分组,每组都有一半的数据行。

你或许会问自己,,如果数据行数不能被NTILE函数的interger_expression参数整除,若产生一个余数,那么情况会是怎样的?当这种情形发生时,每一个余下的行会被放置在每个分组中,从第一个分组开始,直至所有的余数行都被分配给每个分组,如同你在列表5中将会看到的那样。列表5不仅演示了当integer_expression导致了一个不平均的数据行的分配时候会发生什么,而且其还演示了如何使得integer_expression成为一个本地变量。

让我们查看列表5的代码以及结果5的输出来定义SQL SERVER 在数据行数不能被平均分给各个分组时,它是如何处理创建分组的。

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);

列表5:带有多余记录数的NTile查询

当我运行列表5,我将得到结果5的结果。

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
 结果5:运行列表5产生的输出

   在列表5的代码中我定义了一个名为@Integer_Expression的本地变量,并给它赋值为4。接下来在对NTile函数的调用中我使用了这个变量,指定将返回4个分组。如果你查看输出你将发现SQL SERVER创建了四个不同的分组。
当你用10除以4时,你将得到一个余数2。这意味着前两个分组应该比后两个分组多一个数据行。通过查看列表5的输出你可以确认这点。在这个输出中你将看到1组和2组都包含了3行数据,而3组和4组则只有两行数据。 和Rank函数一样,你可以通过在你的NTile函数调用中包含PARTITION BY子句来在每一个分区中创建NTILE排序值。当你在NTILE函数中添加PARTITION BY子句时,SQL SERVER会对每一个分区从1开始NTILE排序值。为了演示这个,让我来运行列表6的代码。
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);

列表6:NTile查询

当我运行列表6,我得到了结果6的输出。

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

结果6:运行列表6的输出

如果你查看结果6的输出,你便能看到由于添加了PARTITION BY 子句到NTILE函数调用产生的效果。如果你查看输出列NTileValue的列值,你会发现对于StateProvinceID为46的行,它们的NTILE排序值又重新从1开始。这便是因为你在我的NTILE函数调用时添加了“PARTITION BY StateProvinceID”子句。

使用ROW_NUMBER函数的例子

有一些时候,你只想为你的输出的每一行生成一个行号,在这里,对于结果集中的每一个新行,其行号总是按顺序递增1的。为了完成这个,我们能够使用ROW_NUMBER函数。

以下是ROW_NUMBER函数的句法:

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

其中:

  • <partition_expressions>: 定义了一个或者多个表达式,其用来对数据进行分区
  • <order by column>:定义了一个或者多个列表,其用来对各个分区的输出进行排序

为了演示ROW_NUMBER函数是如何工作的,我会运行列表7的代码:

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

列表7:使用ROW_NUMBER函数

当我运行列表7,我得到了结果7的结果。

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

结果7:运行列表7的代码产生的输出

通过查看结果7的输出,你将看到我的每一行数据都会有一个唯一的RowNumber列值。RowNumber列值开始于1,并且对于每一行都是按1递增。当我调用ROW_NUMBER函数时,你可以看到我仅仅指定了ORDER BY 子句。在我的示例中,我按PostalCode列进行排序,因为我没有PARTITION BY子句,ROW_NUMBER函数对于每个行返回一个不同的RowNumber值。

假设你仍然按PostalCode排序,但是你又希望对每个新的StateProvinceID,RowNumber都会从1开始,为了实现这个目的,我将给我的查询中添加PARTITION BY子句,如同我在列表8中所做的那样:

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);

列表8:在ROW_NUMBER函数中使用PARTITION BY 子句的查询

当我运行列表8,我得到了结果8的结果

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

结果8:从运行列表8得到的输出

如同你在结果8中所看到的,通过ow向我的查询中添加PARTITION BY子句,对于每一个StateProvinceID,其RowNumber列值都会从1重新开始。

总结

我们有许多中方法对数据进行排序,并且其中一些方法需要你分配应该序列数。在本节中我向你展示了ROW_NUMBER, NTILE, RANK, DENSE_RANK是如何产生一个有序的列值的,下次当你需要产生一个有序的列值时,你应该考虑使用这些排序函数中的某一个。

问题与答案

问题1:

下列那个排序函数允许你指定一个整型表达式,其用来定义你将要使数据拆分为的分组数?

  • RANK
  • DENSE_RANK
  • NTILE
  • ROW_NUMBER

问题2:

DENSE_RANK 函数做了什么不同与RANK函数的事情?

  • 当有重复值时,其产生排序值会跳过一些值
  • 当有重复值时,其产生排序值不会跳过一些值

问题3:

下列哪一个子句会导致对于不同的分组排序值会从1开始?

  • ORDER BY
  • OVER
  • PARTITION BY

答案

问题1:

正确的答案是C,NTILE函数使用提供的整型值来将你的数据分割为几个组。

问题2:

正确答案是B,当遇见重复的值时,DENSE_RANK不会跳过有序的值。

问题3:

正确答案是C,PARTITON BY子句告诉SQLSERVER 每次遇到新的分区列值返回,排序值都会从1开始。

【译】高级T-SQL进阶系列 (七)【下篇】:使用排序函数对数据进行排序的更多相关文章

  1. 【译】高级T-SQL进阶系列 (七)【上篇】:使用排序函数对数据进行排序

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文链接:传送门. 什么是排序函数(Ranking Functions)? 排序函数基于一组记录的集合返回一个排序值.一个排序值其实 ...

  2. Bing Maps进阶系列七:Bing Maps功能导航菜单华丽的变身

    Bing Maps进阶系列七:Bing Maps功能导航菜单华丽的变身 Bing Maps Silverlight Control所提供的功能导航是非常强大的,在设计上对扩展的支持非常好,提供了许多用 ...

  3. 深入理解javascript函数进阶系列第一篇——高阶函数

    前面的话 前面的函数系列中介绍了函数的基础用法.从本文开始,将介绍javascript函数进阶系列,本文将详细介绍高阶函数 定义 高阶函数(higher-order function)指操作函数的函数 ...

  4. 【SQL必知必会笔记(2)】检索数据、排序检索数据

    上个笔记中介绍了一些关于数据库.SQL的基础知识,并且创建我们后续练习所需的数据库.表以及表之间的关系,从本文开始进入我们的正题:SQL语句的练习. 文章目录 1.检索数据(SELECT语句) 1.1 ...

  5. Linq To Sql进阶系列(六)用object的动态查询与保存log篇

    动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处.而Linq的推出,是为了弥补编程中的 Data != Object 的问题.我们又该如何实现用object的动 ...

  6. SQL进阶系列之7用SQL进行集合运算

    写在前面 集合论是SQL语言的根基,因为这种特性,SQL也被称为面向集合语言 导入篇:集合运算的几个注意事项 注意事项1:SQL能操作具有重复行的集合(multiset.bag),可以通过可选项ALL ...

  7. SQL进阶系列之12SQL编程方法

    写在前面 KISS -- keep it sweet and simple 表的设计 注意命名的意义 英文字母 + 阿拉伯数字 + 下划线"_" 属性和列 编程的方针 写注释 注意 ...

  8. SQL进阶系列之10HAVING子句又回来了

    写在前面 HAVING子句的处理对象是集合而不是记录 各队,全队点名 --各队,全体点名! CREATE TABLE Teams (member CHAR(12) NOT NULL PRIMARY K ...

  9. SQL进阶系列之11让SQL飞起来

    写在前面 SQL的性能优化是数据库使用者必须面对的重要问题,本节侧重SQL写法上的优化,SQL的性能同时还受到具体数据库的功能特点影响,这些不在本节讨论范围之内 使用高效的查询 参数是子查询时,使用E ...

随机推荐

  1. [AtCoder]Grand Contest 028

    A Two Abbreviations 题意:给定两个串,长度为\(N\)的\(A\)和长度为\(M\)的\(B\),一个串\(S\)被称为好的,当且仅当:这个串的长度\(L\)能被\(N,M\)整除 ...

  2. 如何去官网上下载tomcat的linux版本

    1.首先进入官网,进入如下位置 2.进入bin文件夹中下载: 原文链接:https://blog.csdn.net/zdb292034/article/details/82433072

  3. Lombok(浅看,自用)

    Lombok 首先是几个常用的注解(最常用到的方法,超简单的用) @Data @AllArgsConstructor @NoArgsConstructor public class Trial_Pro ...

  4. group by分组后对组内数据进行排序

    查询 每个班级英语成绩最高的前两名的记录 原文:https://www.cnblogs.com/hxfcodelife/p/10226934.html select a.Classid,a.Engli ...

  5. Dreamoon and WiFi

    Dreamoon is standing at the position 0 on a number line. Drazil is sending a list of commands throug ...

  6. html代码分享

    贴图:<img src="图片URL"> 加入连接:<a href="所要连接的相关URL">写上你想写的字</a> 在新窗 ...

  7. layer.open 回调函数

    官方资料:http://www.layui.com/doc/modules/layer.html 在一个弹出框中新增个按钮,点击按钮后执行自己的语句(返回上一页并刷新). layer.open({ti ...

  8. 题解 SP5271 XOINC - A Coin Game

    SP5271 XOINC - A Coin Game 双倍经验:P2964 [USACO09NOV]硬币的游戏A Coin Game O3做法(TLE):枚举i,j,k,即剩下i枚金币,上一轮选了j枚 ...

  9. Flask 教程 第十八章:Heroku上的部署

    本文翻译自The Flask Mega-Tutorial Part XVIII: Deployment on Heroku 这是Flask Mega-Tutorial系列的第十八部分,我将在其中部署M ...

  10. Linux Centos7文件系统

    上期教大家创建分区,刚分区完成后没有文件系统,分区不能使用.本期教大家创建文件系统,(文件系统:操作系统通过文件系统管理文件及数据,创建文件系统的过程俗称格式化.)没有文件系统的设备称之为裸(raw) ...