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

原文链接:传送门

使用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. Visibility Graph Analysis of Geophysical Time Series: Potentials and Possible Pitfalls

    Tasks: invest papers  3 篇. 研究主动权在我手里.  I have to.  1. the benefit of complex network: complex networ ...

  2. guava的简单使用

    引入依赖 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId ...

  3. JS 抖动函数封装

    原生JS实现封装的抖动函数框架 <style> ul{ margin-top: 100px; } li { float: left; margin-left: 20px; position ...

  4. IDEA与Tomcat相关配置

    idea会为每一个Tomcat部署的项目,独立建一份配置文件. 配置文件所在位置 怎么部署的(查看虚拟目录)使用的第三种部署方式 部署项目存放的路径 项目目录和Tomcat部署目录 Tomcat真正访 ...

  5. SQLServer使用链接服务器远程查询

    --创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ', 'SQLOLEDB ', '远程服务器名或ip地址 ' exec sp_addlinkedsrvlogi ...

  6. 【译】用 `Wasmer` 进行插件开发 1

    [译]用 Wasmer 进行插件开发 1 Using Wasmer for Plugins Part 1 译文 原文链接 https://wiredforge.com/blog/wasmer-plug ...

  7. Python入门3 —— 基本数据类型

    一:为何变量值(记录的数据)要有类型呢? 1.既然可以记录事物的状态,为什么要分类型呢? 变量值是来记录事物状态的, 而事物的状态是多种多样的, 所以对应着就要应该用不同类型的值去记录这些状态. 二: ...

  8. AcWing 908. 最大不相交区间数量

    //1.将每个区间按右端点从小到大排序 //2.从前往后依次枚举每个区间,如果当前区间中已经包含点,就直接跳过,否则,选择当前区间的右端点 //选右端点的话,可以尽可能的包含在多个区间里 //那么选的 ...

  9. JAVA变量声明在循环体内还是循环体外

    (1) for (int i = 0; i < 10000; ++i) { Object obj = new Object(); System.out.println("obj= &q ...

  10. 解决报错WARNING: IPv4 forwarding is disabled. Networking will not work.

    报错: [root@localhost /]# docker run -it ubuntu /bin/bash WARNING: IPv4 forwarding is disabled. Networ ...