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

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

原文链接:传送门

使用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开始。

原文地址:https://www.cnblogs.com/qianxingmu/p/12264899.html