sql2005

http://blog.youkuaiyun.com/htl258/article/details/4006668

SQL Server 2005 引入几个新的排序(排名)函数,如ROW_NUMBER、RANK、DENSE_RANK等。
这些新函数使您可以有效地分析数据以及向查询的结果行提供排序值。

-- ------------------------------------------------------------------------
ROW_NUMBER()

说明:返回结果集分区内行的序列号,每个分区的第一行从
1 开始。
语法:ROW_NUMBER ()
OVER ( [ <partition_by_clause> ] < order_by_clause > ) 。
备注:
ORDER BY 子句可确定在特定分区中为行分配唯一 ROW_NUMBER 的顺序。
参数:
< partition_by_clause > :将 FROM 子句生成的结果集划入应用了 ROW_NUMBER 函数的分区。
     
< order_by_clause > :确定将 ROW_NUMBER 值分配给分区中的行的顺序。
返回类型:
bigint

示例:


USE AdventureWorks
GO
SELECT c.FirstName, c.LastName, ROW_NUMBER() OVER ( ORDER BY SalesYTD DESC ) AS ' Row Number ' , s.SalesYTD, a.PostalCode
FROM Sales.SalesPerson s JOIN Person.Contact c on s.SalesPersonID = c.ContactID
JOIN Person.Address a ON a.AddressID = c.ContactID
WHERE TerritoryID IS NOT NULL AND SalesYTD <> 0



USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER()
OVER ( order by OrderDate) as RowNumber
FROM Sales.SalesOrderHeader )
SELECT *
FROM OrderedOrders
WHERE RowNumber between 50 and 60 ;


-- ------------------------------------------------------------
RANK()

说明:返回结果集的分区内每行的排名。行的排名是相关行之前的排名数加一。
语法:RANK ()
OVER ( [ < partition_by_clause > ] < order_by_clause > )
备注:如果两个或多个行与一个排名关联,则每个关联行将得到相同的排名。
      例如,如果两位顶尖销售员具有同样的 SalesYTD 值,他们将并列第一。
      由于已有两行排名在前,所以具有下一个最大 SalesYTD 的销售人员将排名第三。
      因此,RANK 函数并不总返回连续整数。
      用于整个查询的排序顺序决定了行在结果集中的显示顺序。这也隐含了行在每个分区中的排名。
参数:
< partition_by_clause > :将 FROM 子句生成的结果集划分为要应用 RANK 函数的分区。
     
< order_by_clause > :确定将 RANK 值应用于分区中的行时所基于的顺序。
返回类型:
bigint

示例:


-- 接上.
--
-----------------------------------------------------------------------------------
DENSE_RANK()

说明:返回结果集分区中行的排名,在排名中没有任何间断。行的排名等于所讨论行之前的所有排名数加一。
语法:DENSE_RANK ()
OVER ( [ < partition_by_clause > ] < order_by_clause > )
备注:如果有两个或多个行受同一个分区中排名的约束,则每个约束行将接收相同的排名。
      例如,如果两位顶尖销售员具有相同的 SalesYTD 值,则他们将并列第一。
      接下来 SalesYTD 最高的销售人员排名第二。该排名等于该行之前的所有行数加一。
      因此,DENSE_RANK 函数返回的数字没有间断,并且始终具有连续的排名。
      整个查询所用的排序顺序确定了各行在结果中的显示顺序。这说明排名第一的行可以不是分区中的第一行。
参数:
< partition_by_clause > :将 FROM 子句所生成的结果集划分为数个将应用 DENSE_RANK 函数的分区。
     
< order_by_clause > :确定将 DENSE_RANK 值应用于分区中各行的顺序。
返回类型:
bigint

示例:

USE AdventureWorks;
GO
SELECT   i.ProductID, p.Name, i.LocationID, i.Quantity, DENSE_RANK() OVER (PARTITION BY i.LocationID order by i.Quantity) as DENSE_RANK
FROM Production.ProductInventory i JOIN Production.Product p ON i.ProductID = p.ProductID
ORDER BY Name;
GO

将上面三个函数放在一起计算,更能明显看出各个函数的功能。

CREATE TABLE rankorder(orderid INT ,qty INT )
INSERT rankorder VALUES ( 30001 , 10 )
INSERT rankorder VALUES ( 10001 , 10 )
INSERT rankorder VALUES ( 10006 , 10 )
INSERT rankorder VALUES ( 40005 , 10 )
INSERT rankorder VALUES ( 30003 , 15 )
INSERT rankorder VALUES ( 30004 , 20 )
INSERT rankorder VALUES ( 20002 , 20 )
INSERT rankorder VALUES ( 20001 , 20 )
INSERT rankorder VALUES ( 10005 , 30 )
INSERT rankorder VALUES ( 30007 , 30 )
INSERT rankorder VALUES ( 40001 , 40 )
INSERT rankorder VALUES ( 30007 , 30 )
GO
-- 对一个列qty进行的排序
SELECT orderid,qty,
       ROW_NUMBER()
OVER ( ORDER BY qty) AS rownumber,
       RANK()      
OVER ( ORDER BY qty) AS rank,
       DENSE_RANK()
OVER ( ORDER BY qty) AS denserank
FROM rankorder
ORDER BY qty


-- 对两个列qty,orderid进行的排序
SELECT orderid,qty,
       ROW_NUMBER()
OVER ( ORDER BY qty,orderid) AS rownumber,
       RANK()      
OVER ( ORDER BY qty,orderid) AS rank,
       DENSE_RANK()
OVER ( ORDER BY qty,orderid) AS denserank
FROM rankorder
ORDER BY qty,orderid
drop table rankorder

-- 示例数据
CREATE TABLE tb(Name varchar ( 10 ),Score decimal ( 10 , 2 ))
INSERT tb SELECT ' aa ' , 99
UNION ALL SELECT ' bb ' , 56
UNION ALL SELECT ' cc ' , 56
UNION ALL SELECT ' dd ' , 77
UNION ALL SELECT ' ee ' , 78
UNION ALL SELECT ' ff ' , 76
UNION ALL SELECT ' gg ' , 78
UNION ALL SELECT ' ff ' , 50
GO

-- 1. 名次生成方式1,Score重复时合并名次
SELECT * ,Place = ( SELECT COUNT ( DISTINCT Score) FROM tb WHERE Score >= a.Score)
FROM tb a
ORDER BY Place




-- 2. 名次生成方式2,Score重复时保留名次空缺
SELECT * ,Place = ( SELECT COUNT (Score) FROM tb WHERE Score > a.Score) + 1
FROM tb a
ORDER BY Place

create table cj(bj int ,zf int )
insert into cj select 1 , 98
insert into cj select 2 , 97
insert into cj select 1 , 96
insert into cj select 2 , 96
insert into cj select 1 , 95
insert into cj select 2 , 94
insert into cj select 1 , 94
insert into cj select 2 , 94
insert into cj select 1 , 93

select bj,
     zmc
= ( select count ( distinct zf) from cj where zf > a.zf) + 1 ,
     bmc
= ( select count ( distinct zf) from cj where zf > a.zf and bj = a.bj) + 1 ,
     zf
from cj a

go
drop table cj

 

 

 

SQL server 2005新增的几个函数,分别是row_number( )rank( )、 ,DENSE_RANK( )ntile( ) 下面以实例分别简单讲解。

1.row_number( )
         先来点数据,先建个表

SET NOCOUNT ON
CREATE TABLE Person(
FirstName VARCHAR(10),
Age INT,
Gender CHAR(1))
INSERT INTO Person VALUES ('Ted',23,'M')
INSERT INTO Person VALUES ('John',40,'M')
INSERT INTO Person VALUES ('George',6,'M')
INSERT INTO Person VALUES ('Mary',11,'F')
INSERT INTO Person VALUES ('Sam',17,'M')
INSERT INTO Person VALUES ('Doris',6,'F')
INSERT INTO Person VALUES ('Frank',38,'M')
INSERT INTO Person VALUES ('Larry',5,'M')
INSERT INTO Person VALUES ('Sue',29,'F')
INSERT INTO Person VALUES ('Sherry',11,'F')
INSERT INTO Person VALUES ('Marty',23,'F')
直接用例子说明问题:
SELECT ROW_NUMBER() OVER (ORDER BY Age) AS [Row Number by Age],
FirstName,
Age
FROM Person

出现的数据如下
Row Number by Age                              FirstName                      Age
--------------------------                                ----------                      --------
                                                                                            Larry                                    5
                                                                                            Doris                                    6
                                                                                            George                            6
                                                                                            Mary                                    11
                                                                                            Sherry                                11
                                                                                            Sam                                      17
                                                                                            Ted                                        23
                                                                                            Marty                                    23
                                                                                            Sue                                        29
10                                                                                          Frank                                  38
11                                                                                          John                                      40
可以观察到,是根据年龄升序排列了,并且row_number()是给出了序列号了,这个序列号被重命名为Row Number by Age,
sql server2000





对比:
如果在sql server2000中实现相对麻烦一些,我们可以利用IDENTITY()函数实现,但IDENTITY()函数只能用在sql server2000临时表中,因此需要将数据检索到临时表里。
select identity(int,1,1) as [Row Number by Age]


,FirstName,Age into #A from Person order by Age
select * from #A
drop table #a


如果不想按年龄排序,可以这样写
SELECT ROW_NUMBER() OVER


 (ORDER BY (SELECT 1)) AS [Row Number by Record Set],
FirstName,
Age
FROM Person
另外一个例子
SELECT ROW_NUMBER() OVER


 (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],
FirstName,
Age,
Gender
FROM Person
这里是按性别划分区间了,同一性别再按年龄来排序,输出结果如下
Partition by Gender         FirstName         Age                Gender
-------------------- ---------- ----------- ------
1                           Doris                            F
2                           Mary              11                 F
3                           Sherry            11                 F
4                           Sue               29                 F
1                           Larry                            M
2                           George                           M
3                           Sam               17                 M
4                           Ted               23                 M
5                           Marty             23                 M
6                           Frank             38                 M
7                           John              40                 M
注意,姓名M开始,序号又从1,2,3开始了
 
2.


RANK( )


函数
         先看例子
SELECT RANK() OVER


 (ORDER BY Age) AS [Rank by Age],
FirstName,
Age
FROM Person
输出如下:
Rank by Age                 FirstName         Age
-------------------- ---------- -----------
1                           Larry             5
2                           Doris             6
2                           George            6
4                           Mary              11
4                           Sherry            11
6                           Sam               17
7                           Ted               23
7                           Marty             23
9                           Sue               29
10                          Frank             38
11                          John              40
看到了么,同年岭的话,将有相同的顺序,顺序成1,2,2,4了。
sql server2000





对比:
出现了RANK()函数实在是方便,在sql server2000里实现排序并列的问题麻烦很多。
select [Rank by Age


]=isnull((select count(*) from person where Age>A.Age),0)+1,FirstName,Age from Person A order by [Rank by Age


]


 
SELECT RANK() OVER


(PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],
FirstName, Age, Gender FROM Person输出为
Partition by Gender         FirstName         Age                Gender
-------------------- ---------- ----------- ------
1                           Doris                            F
2                           Mary              11                 F
2                           Sherry            11                 F
4                           Sue               29                 F
1                           Larry                            M
2                           George                           M
3                           Sam               17                 M
4                           Ted               23                 M
4                           Marty             23                 M
6                           Frank             38                 M
7                           John              40                 M

可以看到,按性别分组了,每个性别分组里,继续是用了rank( )函数

3. DENSE_RANK( ) 函数
         SELECT DENSE_RANK() OVER (ORDER BY Age) AS [Dense Rank by Age],
         FirstName,
         Age
         FROM Person

输出结果为:
Dense Rank by Age          FirstName        Age
-------------------- ---------- -----------
                         Larry            5
                         Doris            6
                         George           6
                         Mary             11
                         Sherry           11
                         Sam              17
                         Ted              23
                         Marty            23
                         Sue              29
                         Frank            38
                         John             40


看到了么,和rank函数区别是,顺序始终是连续的,Doris 和George同年,都是排第2位,但之后的mary不象rank函数那样排第4,而是排第3位了


4. ntile( ) 函数
SELECT FirstName,
Age,
NTILE(3) OVER (ORDER BY Age) AS [Age Groups]
FROM Person

输出结果:
FirstName        Age               Age Groups
---------- ----------- --------------------
Larry                                1
Doris                                1
George                            1
Mary                11                1
Sherry             11                 2
Sam                17                 2
Ted                 23                 2
Marty              23                 2
Sue                29                 3
Frank             38                 3
John               40                 3
这个函数按照ntile(n)中的N,把记录强制分成多少段,11条记录现在分成3段了,lary到mary是第1段,sherry到maty是第2段,sue到john是第3段了。

 

 

排名函数是 SQL Server2005新加的功能。在 SQL Server2005中有如下四个排名函数:

1. row_number

2. rank

3. dense_rank

4. ntile   
    下面分别介绍一下这四个排名函数的功能及用法。在介绍之前假设有一个t_table表,表结构与表中的数据如图1所示:



 
select  row_number()  over ( order   by  field1)  as  row_number, *   from  t_table

 

    上面的 SQL语句的查询结果如图 2所示。



 

图2
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    其中 row_number列是由 row_number函数生成的序号列。在使用 row_number函数是要使用 over子句选择对某一列进行排序,然后才能生成序号。

    实际上, row_number函数生成序号的基本原理是先使用 over子句中的排序语句对记录进行排序,然后按着这个顺序生成序号。 over子句中的 order by子句与 SQL语句中的 order by子句没有任何关系,这两处的 order by 可以完全不同,如下面的 SQL语句所示:

 

select  row_number()  over ( order   by  field2  desc as  row_number, *   from  t_table  order   by  field1  desc

 

    上面的 SQL语句的查询结果如图 3所示。



 

图3
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }      我们可以使用row_number函数来实现查询表中指定范围的记录,一般将其应用到Web应用程序的分页功能上。下面的SQL语句可以查询t_table表中第2条和第3条记录:

 

with  t_rowtable
as
(
    
select  row_number()  over ( order   by  field1)  as  row_number, *   from  t_table
)
select   *   from  t_rowtable  where  row_number > 1   and  row_number  <   4   order   by  field1

 

p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }    上面的SQL语句的查询结果如图4所示。



 

图4
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }      上面的 SQL 语句使用了 CTE ,关于 CTE 的介绍将读者参阅 《SQL Server2005杂谈(1):使用公用表表达式(CTE)简化嵌套SQL》
      另外要注意的是,如果将 row_number 函数用于分页处理, over 子句中的 order by 与排序记录的 order by 应相同,否则生成的序号可能不是有续的。
      当然,不使用 row_number 函数也可以实现查询指定范围的记录,就是比较麻烦。一般的方法是使用颠倒 Top 来实现,例如,查询 t_table 表中第 2 条和第 3 条记录,可以先查出前 3 条记录,然后将查询出来的这三条记录按倒序排序,再取前 2 条记录,最后再将查出来的这 2 条记录再按倒序排序,就是最终结果。 SQL 语句如下:

 

 

select   *   from  ( select   top   2   *   from select   top   3   *   from  t_table  order   by  field1)  order   by  field1  desc order   by  field1

 

    上面的 SQL语句查询出来的结果如图 5所示。



 

图5
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    这个查询结果除了没有序号列 row_number,其他的与图 4所示的查询结果完全一样。

二、 rank

    rank 函数考虑到了 over子句中排序字段值相同的情况,为了更容易说明问题,在 t_table表中再加一条记录,如图 6所示。




 
图6
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }       在图6所示的记录中后三条记录的field1字段值是相同的。如果使用rank函数来生成序号,这3条记录的序号是相同的,而第4条记录会根据当前的记录数生成序号,后面的记录依此类推,也就是说,在这个例子中,第4条记录的序号是4,而不是2。rank函数的使用方法与row_number函数完全相同,SQL语句如下:

 

select  rank()  over ( order   by  field1), *   from  t_table  order   by  field1

 

p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    上面的 SQL语句的查询结果如图 7所示。



 

图7
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

三、 dense_rank

    dense_rank 函数的功能与 rank函数类似,只是在生成序号时是连续的,而 rank函数生成的序号有可能不连续。如上面的例子中如果使用 dense_rank函数,第 4条记录的序号应该是 2,而不是 4。如下面的 SQL语句所示:

 

 

 

select  dense_rank()  over ( order   by  field1), *   from  t_table  order   by  field1

 

 

 

p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }    上面的 SQL语句的查询结果如图 8所示。



 

图8
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    读者可以比较图 7和图 8所示的查询结果有什么不同

 

 

四、ntile
      ntile函数可以对序号进行分组处理。这就相当于将查询出来的记录集放到指定长度的数组中,每一个数组元素存放一定数量的记录。ntile函数为每条记录生成的序号就是这条记录所有的数组元素的索引(从1开始)。也可以将每一个分配记录的数组元素称为“桶”。ntile函数有一个参数,用来指定桶数。下面的SQL语句使用ntile函数对t_table表进行了装桶处理:

 

 

select  ntile( 4 over ( order   by  field1)  as  bucket, *   from  t_table

 

p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    上面的 SQL语句的查询结果如图 9所示。



 

图9
p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    由于 t_table表的记录总数是 6,而上面的 SQL语句中的 ntile函数指定了桶数为 4。

    也许有的读者会问这么一个问题, SQL Server2005怎么来决定某一桶应该放多少记录呢?可能 t_table表中的记录数有些少,那么我们假设 t_table表中有 59条记录,而桶数是 5,那么每一桶应放多少记录呢?

    实际上通过两个约定就可以产生一个算法来决定哪一个桶应放多少记录,这两个约定如下:

1. 编号小的桶放的记录不能小于编号大的桶。也就是说,第 1捅中的记录数只能大于等于第 2桶及以后的各桶中的记录。

2. 所有桶中的记录要么都相同,要么从某一个记录较少的桶开始后面所有捅的记录数都与该桶的记录数相同。也就是说,如果有个桶,前三桶的记录数都是 10,而第 4捅的记录数是 6,那么第 5桶和第 6桶的记录数也必须是 6。

    根据上面的两个约定,可以得出如下的算法:

 

 

     //  mod表示取余,div表示取整 
     if (记录总数 mod 桶数  ==   0 )
    {
        recordCount 
=  记录总数 div 桶数;
        将每桶的记录数都设为recordCount
    
    
else
    {
        recordCount1 
=  记录总数 div 桶数  +   1 ;
        
int   =   1   //   n表示桶中记录数为recordCount1的最大桶数
         =  recordCount1  *  n;
        
while (((记录总数  -  m)  mod  (桶数  -   n))   !=   0  )
        {
            n
++ ;
            
=  recordCount1  *  n;
        
        recordCount2 
=  (记录总数  -  m) div  (桶数  -  n);
        将前n个桶的记录数设为recordCount1
        将n 
+  1个至后面所有桶的记录数设为recordCount2
    }

 

p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman"; }div.Section1 { page: Section1; }

    根据上面的算法,如果记录总数为 59,桶数为 5,则前 4个桶的记录数都是 12,最后一个桶的记录数是 11。

    如果记录总数为 53,桶数为 5,则前 3个桶的记录数为 11,后 2个桶的记录数为 10。

    就拿本例来说,记录总数为 6,桶数为 4,则会算出 recordCount1的值为 2,在结束 while循环后,会算出 recordCount2的值是 1,因此,前 2个桶的记录是 2,后 2个桶的记录是 1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值