zoukankan      html  css  js  c++  java
  • 【译】高级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开始。

  • 相关阅读:
    经典网络还是VPC,开发者作何选择?
    经典网络还是VPC,开发者作何选择?
    文件系统的几种类型:ext3, swap, RAID, LVM
    文件系统的几种类型:ext3, swap, RAID, LVM
    ★商场上的十则寓言故事!
    ★商场上的十则寓言故事!
    【★】自制网络心理需求大排名!
    【★】自制网络心理需求大排名!
    自制mpls ldp实验
    自制mpls ldp实验
  • 原文地址:https://www.cnblogs.com/qianxingmu/p/12264899.html
Copyright © 2011-2022 走看看