T-SQL Enhancement in SQL Server 2005[下篇]

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
简介:
第一部分中,我们讨论了APPLYCTE这两个T-SQL Enhancement。APPLY实现了Table和TVF的Join,CTE通过创建“临时的View”的方式使问题化繁为简。现在我们接着来讨论另外两个重要的T-SQL Enhancement Items:PIVOTRanking。 

三、            PIVOT Operator

PIVOT的中文意思是“在枢轴上转动”,比如对于一个2维坐标,将横坐标变成纵坐标,将纵坐标变成横坐标。反映在一个Relational Table上的意思就是:变成为列,变列为行。相信大家在进行报表设计的时候都遇到过类似于这样的需求:统计2002年内某个销售人员第一季度每个月处理的订单数。在AdventureWorks Sample Databse中,Sales Order存储于SaleOrderHeader这张表中,它的结果如下:



我们一般情况下通过下面的
SQL实现我们提出的统计功能:

SELECT SalesPersonID,
SUM( CASE  DATEPART(MM,OrderDate) WHEN  1  THEN  1  ELSE  0  ENDAS JAN,
SUM( CASE  DATEPART(MM,OrderDate) WHEN  2  THEN  1  ELSE  0  ENDAS FEB,
SUM( CASE  DATEPART(MM,OrderDate) WHEN  3  THEN  1  ELSE  0  ENDAS MAR,
SUM( CASE  DATEPART(MM,OrderDate) WHEN  4  THEN  1  ELSE  0  ENDAS APR
FROM Sales.SalesOrderHeader
WHERE  DATEPART(yyyy,OrderDate)  =  2002
GROUP  BY SalesPersonID

于是我们得到了这样的统计数据:



通过数据在原表的结构和我们最终获得的结果进行比较,我们发现就像是“旋转”了
90度,原来的OrderDate是存储在每行的基于Order的一个属性(行),现在我们要把Order Date按照不同月份统计,这样行变成了列。

像这样的需求,我们都可以可以通过PIVOT这个操作符来实现,下面就是基于PIVOTSQL

SELECT SalesPersonID,  [ 1 ]  AS JAN, [ 2 ]  AS FEB,  [ 3 ]  AS MAR,  [ 4 ]  AS APR
FROM 
(
     SELECT SalesPersonID,  DATEPART(MM,OrderDate)  AS MON
     FROM Sales.SalesOrderHeader
     WHERE  DATEPART(yyyy,OrderDate)  =  2002
) S

PIVOT 
(
     COUNT(MON)  FOR MON  IN ( [ 1 ], [ 2 ], [ 3 ], [ 4 ])
)
AS P

在上面的例子中,同过下面的SELECT语句筛选出来的是为经过PIVOT的数据。

SELECT SalesPersonID,  DATEPART(MM,OrderDate)  AS MON
     FROM Sales.SalesOrderHeader
     WHERE  DATEPART(yyyy,OrderDate)  =  2002

通过下面的PIVOTCOUNT(MON)是我们需要统计的数据,FOR MON IN ([1],[2],[3],[4]是统计的范围)就成了我们最终输出的结构了。
 

PIVOT 
(
     COUNT(MON)  FOR MON  IN ( [ 1 ], [ 2 ], [ 3 ], [ 4 ])
)

如果你第一次见到PIVOT,可以不能一下明白它的实现,但是只要你是使用了一两次,相信就会很容易地掌握它。与PIVOT对应的还以一个操作符UNPIVOT,它完成PIVOT的逆操作,在这里就不介绍了,如果有兴趣的话,可以参考SQL Server Books Online

四、            Ranking

排序与排名是我们最为常用的统计方式,比如对班级的学生根据成员进行排名,或者按照成绩高低把学生划分成若干梯队:比如最好成绩的10名学生属于第一梯队,后10名又划分为第二梯队,以此类推。Ranking设计的Key Words包括:ROW_NUMBER(),RANK(),DENSE_RANK(),NTILE()我们现在就来介绍一下他们的用法和相互之间的差异。

1.          1. ROW_NUMBER()

看到ROW_NUMBER(),我想绝大多数人会像想到OracleROWNUM。他们的作用相似,都是表示某条记录所处的IndexROW_NUMBER()OracleROWNUM更加强大的是,它可以通过OVER语句指定一个进行排序的Column,比如:ROW_NUMBER() OVER (ORDER BY CustomerID)

我们来看一个例子:对Sales.SalesOrderHeader按照CustomerID进行排序,并显示每条记录的Row Number

SELECT SalesOrderID,CustomerID,ROW_NUMBER()  OVER ( ORDER  BY CustomerID)  AS RowNum
FROM Sales.SalesOrderHeader

下面是查询结果:



我们发现最终的结果按照
CutomerID进行排序,RowNum1开始以此递增,每条记录(不管是否具有相同的CustomerID)拥有不同的RowNum

提到排序,我们就不得不提到Order BY,如果我们在后面加上ORDER BY,并指定不同的排序字段,会出现怎样的结果呢?

SELECT SalesOrderID,CustomerID,ROW_NUMBER()  OVER ( ORDER  BY CustomerID)  AS RowNum
FROM Sales.SalesOrderHeader
ORDER  BY SalesOrderID

查询获得的结果是:



从上图中可以看到,最终的结果以
ORDER BY中指定的SalesOrderID进行排序,但是ROW_NUMBER()体现的值却是基于CustmerID排序的。

由于ROW_NUMBER()体现是基于某个确定的字段进行排序后某个DataRow所处的位置,所以它不能直接使用到AggregateColumn中。比如下面的SQL是不合法的:

SELECT CustomerID, COUNT( *AS OrderCount,ROW_NUMBER()  OVER ( ORDER  BY OrderCount)
FROM Sales.SalesOrderHeader
GROUP  BY CustomerID

要是想按照OrderCount,可以使用第一部分介绍的CTE

WITH CTE_Order(CustomerID,OrderCount)
AS
(
SELECT CustomerID, COUNT( *AS OrderCount
FROM Sales.SalesOrderHeader
GROUP  BY CustomerID
)

SELECT CustomerID,OrderCount,ROW_NUMBER()  OVER ( ORDER  BY OrderCount)
FROM CTE_Order

2.          RANK()

RANK()的使用和ROW_NUMBER()类似。不过它与ROW_NUMBER()所不同的是:对于被指定为排序的字段,具有相同值得Row对应的返回值相同。比如:

SELECT SalesOrderID,CustomerID,RANK()  OVER ( ORDER  BY CustomerID)  AS RowNum
FROM Sales.SalesOrderHeader

下面是相应的查询结果:



对于
RANK(),还有一点需要说明的是,它的回返值不是连续的, 比如第五条记录的Row_Num5而不是2。如果想实现这样需求,就需要用下面一个FunctionDENSE_RANK()

3.          3. DENSE_RANK()

DENSE_RANK()实现了一个连续的Ranking。比如下面的SQL

SELECT SalesOrderID,CustomerID,DENSE_RANK()  OVER ( ORDER  BY CustomerID)  AS RowNum
FROM Sales.SalesOrderHeader

就来产生如下的查询结果:



4. NTILE()

上面我们说到划分梯队的问题,这样的问题可以通过NTILE() Function来实现。比如我们现在按照CustomerID排序,把CustomerID12的划分到3梯队中:

SELECT SalesOrderID,CustomerID,NTILE( 3OVER ( ORDER  BY CustomerID)  AS RowNum
FROM Sales.SalesOrderHeader
WHERE CustomerID  < 3

其查询结果为:



我们可以看到,一共
12条记录,划分为3组,平均下来每组4条记录。

5.          5.PARTITION BY

上面提到的所有Ranking都是基于真个结果基的。而有的时候我们需要将真个结果集按照某个Column 进行分组,进行基于组的Ranking。这就需要PARTITION BY了。PARTITION BY置于OVER Clause中,和ORDER BY 平级。

比如下面的SQLOrder记录按照CustomerID进行分组,在每组中输出排名(安OrderDate排序):

SELECT SalesOrderID,CustomerID,RANK()  OVER (PARTITION  BY CustomerID  ORDER  BY OrderDate)  AS RowNum
FROM Sales.SalesOrderHeader

相应的查询结果:



T-SQL Enhancement in SQL Server 2005:
[原创]T-SQL Enhancement in SQL Server 2005 - Part I
[原创]T-SQL Enhancement in SQL Server 2005 - Part II


Reference: 《Programming Microsoft SQL Server 2005》 By Andrew J. Brust & Stephen Forte


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
2月前
|
SQL Web App开发 安全
SQL Server 2025 年 8 月更新 - 修复 CVE-2025-49759 SQL Server 特权提升漏洞
SQL Server 2025 年 8 月更新 - 修复 CVE-2025-49759 SQL Server 特权提升漏洞
167 2
SQL Server 2025 年 8 月更新 - 修复 CVE-2025-49759 SQL Server 特权提升漏洞
|
1月前
|
SQL Web App开发 安全
SQL Server 2025 年 9 月更新 - 修复 CVE-2025-47997 SQL Server 信息泄露漏洞
SQL Server 2025 年 9 月更新 - 修复 CVE-2025-47997 SQL Server 信息泄露漏洞
86 0
SQL Server 2025 年 9 月更新 - 修复 CVE-2025-47997 SQL Server 信息泄露漏洞
|
2月前
|
SQL 容灾 安全
云时代SQL Server的终极答案:阿里云 RDS SQL Server如何用异地容灾重构系统可靠性
在数字化转型的浪潮中,数据库的高可用性已成为系统稳定性的生命线。作为经历过多次生产事故的资深开发者,肯定深知传统自建SQL Server架构的脆弱性——直到遇见阿里云 RDS SQL Server,其革命性的异地容灾架构彻底改写了游戏规则。
|
3月前
|
SQL Web App开发 安全
SQL Server 2025年7月更新 - 修复 CVE-2025-49718 Microsoft SQL Server 信息泄露漏洞
SQL Server 2025年7月更新 - 修复 CVE-2025-49718 Microsoft SQL Server 信息泄露漏洞
304 0
SQL Server 2025年7月更新 - 修复 CVE-2025-49718 Microsoft SQL Server 信息泄露漏洞
|
11月前
|
SQL 开发框架 .NET
突破T-SQL限制:利用CLR集成扩展RDS SQL Server的功能边界
CLR集成为SQL Server提供了强大的扩展能力,突破了T-SQL的限制,极大地拓展了SQL 的应用场景,如:复杂字符串处理、高性能计算、图像处理、机器学习集成、自定义加密解密等,使开发人员能够利用 .NET Framework的丰富功能来处理复杂的数据库任务。
|
SQL 关系型数据库 MySQL
【超全整理】SQL日期与时间函数大汇总会:MySQL与SQL Server双轨对比教学,助你轻松搞定时间数据处理难题!
【8月更文挑战第31天】本文介绍了在不同SQL数据库系统(如MySQL、SQL Server、Oracle)中常用的日期与时间函数,包括DATE、NOW()、EXTRACT()、DATE_ADD()、TIMESTAMPDIFF()及日期格式化等,并提供了具体示例。通过对比这些函数在各系统中的使用方法,帮助开发者更高效地处理日期时间数据,满足多种应用场景需求。
1680 1
|
SQL 运维 监控
SQL Server 运维常用sql语句(二)
SQL Server 运维常用sql语句(二)
126 3
|
SQL 数据库
执行 Transact-SQL 语句或批处理时发生了异常。 (Microsoft.SqlServer.ConnectionInfo)之解决方案
执行 Transact-SQL 语句或批处理时发生了异常。 (Microsoft.SqlServer.ConnectionInfo)之解决方案
1374 0
|
SQL XML 运维
SQL Server 运维常用sql语句(三)
SQL Server 运维常用sql语句(三)
171 1
|
Java 应用服务中间件 Maven
从零到英雄:一步步构建你的首个 JSF 应用程序,揭开 JavaServer Faces 的神秘面纱
【8月更文挑战第31天】JavaServer Faces (JSF) 是一种强大的 Java EE 标准,用于构建企业级 Web 应用。它提供了丰富的组件库和声明式页面描述语言 Facelets,便于开发者快速开发功能完善且易于维护的 Web 应用。本文将指导你从零开始构建一个简单的 JSF 应用,包括环境搭建、依赖配置、Managed Bean 编写及 Facelets 页面设计。
283 0

热门文章

最新文章