较之前一版本,SQL Server 2005可以说是作出了根本性的革新。对于一般的编程人员来说,最具吸引力的一大特性就是实现了对CLR的寄宿,使我们可以使用任意一种.NET Programming Language来编写Stored Procedure、Function、Trigger、User Defined Type等等。但是并不意味着我们使用多年的T-SQL即将被淘汰,而事实上T-SQL仍然是我们最为常见的基于Database的编程语言。为了使编程人员更容易地使用T-SQL来实现一些较为复杂的功能,SQL Server 2005在T-SQL进行了一系列的改进,这篇文章将概括性地介绍这些T-SQL Enhancement。

为了使读者对这些新引入的T-SQL特性有一个大概的了解,我先概括性地列出这些特性:

APPLY Operator
Common Table Expression
PIVOT Operator
TOP Clause Enhancement
Ranking
DDL Trigger
Others

一、APPLY Operator

APPLY这个操作符被置于一个查询的FROM语句中,对于查询出的每条数据行,都去调用一个Table Value Function(TVF),并将TVF的数据附加在现有的查询结果上。APPLY通常用于这样的场景中:查询的结果一部分包含在一个Table或者View中,另一部分则通过一个TVF来获得,通过TVF获得的记录是基于Table或者View中每条记录的某个Column的数据,也就是说我们把Table或者View的某个Column的值作为调用TVF的参数。这实际上将通过TVF获得的Table作为现有Table或者View的Outer table,将它们连接(Join)在一起,而连接它们的Key就是作为TVF参数传入的Column。

我们知道Join分为Inner Join和Outer Join,他们分别对应着CROSS APPLY和OUTER APPLY。如果对于某个条记录,TVF发挥的是一个空的Rowset,对于CROSS APPLY,该记录将不会出现在最终的结果中,而对于OUTER APPLY来说,最终的查询结果将包含该条记录,只是基于TVF的Column的值为NULL。

可能文字描述太过抽象,我们现在通过例子来进一步理解APPLY Operator。下面的例子基于的Database是SQL Server 2005 的Sample Database:AdventureWorks。(注:后续的例子如未作特殊的说明,均使用的是该Database)。我们首先创建一个TVF:dbo.fn_getproduct。根据Product ID获得产品信息。

   1: IF EXISTS (SELECT * FROM sysobjects WHERE type = 'IF' AND name = 'fn_getproduct')
   2:     BEGIN
   3:         DROP  Function  dbo.fn_getproduct
   4:     END
   5: GO
   6:  
   7: CREATE Function dbo.fn_getproduct 
   8: (
   9:     @product_id Int
  10: )
  11: RETURNS TABLE
  12:  
  13: AS RETURN 
  14:  
  15: SELECT * FROM Production.Product WHERE ProductID = @product_id
  16:  
  17: GO

然后我们做如下的查询:对Production.WorkOrder作查询,并列出对应的Product的信息:

   1: SELECT WorkOrderID,WorkOrder.ProductID,ProductNumber,[Name],OrderQty
   2: FROM Production.WorkOrder WorkOrder
   3: CROSS APPLY dbo.fn_getproduct(WorkOrder.ProductID)

下面是查询结果:

我们可以看到ProductNumber和Name两个Column实际上是来自TVF中的,其余才是来自于Production.WorkOrder。如果把TVF看作一个Table,通过查询结果我们可以看出,上面的查询相当于把这个Table和Production.WorkOrder通过ProductID作了一个Join。到底是Inner Join,还是Outer Join?我们对这个TVF作如下修改,使其在正常的情况下返回一个空的结果集(WHERE ProductID = @product_id * -1):

   1: IF EXISTS (SELECT * FROM sysobjects WHERE type = 'IF' AND name = 'fn_getproduct')
   2:     BEGIN
   3:         DROP  Function  dbo.fn_getproduct
   4:     END
   5: GO
   6:  
   7: CREATE Function dbo.fn_getproduct 
   8: (
   9:     @product_id Int
  10: )
  11: RETURNS TABLE
  12:  
  13: AS RETURN 
  14:  
  15: SELECT * FROM Production.Product WHERE ProductID = @product_id * -1
  16:  
  17: GO

再次运行上面的查询,我们会发现最终返回的结果为空:


看来CROSS APPLY使用的是Inner Join。我们现在来试试OUTER APPLY:

   1: SELECT WorkOrderID,WorkOrder.ProductID,ProductNumber,[Name],OrderQty
   2: FROM Production.WorkOrder WorkOrder
   3: OUTER APPLY dbo.fn_getproduct(WorkOrder.ProductID)

下面是最终的输出结果,我们发现所有的Order记录被返回,通过TVF获得的ProductNumber和Name的值为NULL。这充分说明了OUTER APPLY采用的是OUTER JOIN。

二、Common Table Expression

Common Table Expression(CTE)可以看成是一个临时创建的View,他的生命周期仅仅限于当前Context。一旦CTE被创建,你可以将它当成一般的Table,大部分基于Table的操作都可以运用于CTE。下面是创建CTE的语法结构:

   1: WITH cte_name(column name list)
   2: AS
   3: (
   4:      query
   5: )

E.G.

   1: WITH CTE_Black_Product
   2: AS
   3: (
   4:     SELECT * FROM Production.Product WHERE Color = 'Black'
   5: )
   6:  
   7: SELECT * FROM CTE_Black_Product

CTE具有广泛的运用,他往往具有将问题化繁为简的魔力。下面介绍几个典型的运用:

1、将复杂的Aggregate置于CTE中,将复杂的问题分解为多个步骤。

如果我们现在需要统计每个客户发出的订单数量(相关数据存储于Sales.SalesOrderHeader中),同时输出客户的个人信息(相关数据存储于Sales.Customer中)。虽然这样的功能很简单,但他体现了一种思想,把一部完成略显复杂的功能进程分解成多个简单的步骤。

   1: WITH CTE_SalesOrder_Count
   2: AS
   3: (
   4:     SELECT CustomerID, Count(*) As OrderCount
   5:     FROM Sales.SalesOrderHeader
   6:     GROUP BY CustomerID
   7: )
   8:  
   9: SELECT Sales.Customer.CustomerID, AccountNumber,OrderCount
  10: FROM Sales.Customer INNER JOIN CTE_SalesOrder_Count
  11: ON CTE_SalesOrder_Count.CustomerID = Sales.Customer.CustomerID

2、使用CTE代替自连接,以便更易于理解。

假设我们有一个Product表用于存储每个Product的信息,每个Product有一个唯一标识Product_ID和一个不唯一的Product_Name。由于不同的Product可能重名,倘若我们有这样的一个需求:需要将重名的记录(除了具有最小ID的那个)删除,从而保证其名称的唯一性。我们来看看如何保这些需要上出的记录筛选出来。Product表的记录如下,ID为1和4的两条记录重名,现在我们的目的是把ID为4的记录筛选出来。

在不考虑CTE的情况下,我们通过下面的SQL实现这个功能,这个SQL采用了自连接。虽然SQL看起来很简洁,但是相信有一些人第一次看到这样一个SQL,不能立即理解。

   1: SELECT * 
   2: FROM dbo.PRODUCT
   3: WHERE PRODUCT_ID NOT IN
   4: (
   5:     SELECT MIN(PRODUCT_ID)
   6:     FROM dbo.PRODUCT p
   7:     WHERE dbo.PRODUCT.PRODUCT_NAME = p.PRODUCT_NAME
   8: )

但是如果我们采用了CTE,通过下面一段SQL来实现,虽然代码多了点,但是从语义上看要易于理解一点:首先把重名的选出来,在和Product作一次连接。

   1: WITH CTE_PRODUCT(PRODUCT_ID,PRODUCT_NAME)
   2: AS
   3: (
   4:     SELECT MIN(PRODUCT_ID) AS PRODUCT_ID,PRODUCT_NAME
   5:     FROM dbo.PRODUCT
   6:     GROUP BY PRODUCT_NAME
   7:     HAVING COUNT(*)>1
   8: )
   9:  
  10: SELECT dbo.PRODUCT.PRODUCT_ID, dbo.PRODUCT.PRODUCT_NAME
  11: FROM dbo.PRODUCT 
  12: INNER JOIN CTE_PRODUCT
  13: ON CTE_PRODUCT.PRODUCT_NAME = dbo.PRODUCT.PRODUCT_NAME
  14: AND dbo.PRODUCT.PRODUCT_ID > CTE_PRODUCT.PRODUCT_ID

3、  用于具有层次结构记录的递归查询

比如一个公司的员工体系就是一个包含上下级关系的具有层次化的树形结构。假设我们有如下一个EMPLOYEE表,通过REPORT_TO体现每个员工的上下级关系(假设Empoyee_Name具有唯一性)。

我们现在的需求是:列出员工A的所有下级。为了实现这样的一个功能,我们需要以一种特殊的结构来创建CTE:

   1: WITH CTE_EMPLOYEE(EMPLOYEE_ID, EMPLOYEE_NAME,REPORT_TO)
   2: AS
   3: (
   4:     SELECT * 
   5:     FROM dbo.EMPLOYEE
   6:     WHERE EMPLOYEE_NAME = 'A'
   7:     
   8:     UNION ALL
   9:     
  10:     SELECT dbo.EMPLOYEE.*
  11:     FROM dbo.EMPLOYEE
  12:     JOIN CTE_EMPLOYEE
  13:     ON dbo.EMPLOYEE.REPORT_TO = CTE_EMPLOYEE.EMPLOYEE_ID
  14: )
  15:  
  16: SELECT * 
  17: FROM CTE_EMPLOYEE
  18: WHERE EMPLOYEE_NAME > 'A'
  19: OR EMPLOYEE_NAME < 'A'

我们发现CTE中主体部分由两个SELECT语句组成,我们把第一个叫做Anchor Member(AM),AM不会递归,只会执行一次,本例中筛选出了级别最高的A;另一个SELECT语句叫做Recursive Member(RM),RM通过CTE本身和EMPLOYEE表建立连接,所以RM会采用递归的方式执行。
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
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接

T-SQL Enhancement in SQL Server 2005[上篇]相关推荐

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

    在第一部分中,我们讨论了APPLY和CTE这两个T-SQL Enhancement.APPLY实现了Table和TVF的Join,CTE通过创建"临时的View"的方式使问题化繁为 ...

  2. 用SQL Server 2005构建高性能数据仓库

    用SQL Server 2005构建高性能数据仓库 摘要:本文主要讨论当架构一个很大的.高性能的数据仓库,特别是对那种无法预知有多少查询量的系统时要考虑的一些东西.这个讨论包括SQL Server 2 ...

  3. 在SQL Server 2000 和SQL Server 2005中导出表结构

    SQL Server 2000 SELECT     表名       = case when a.colorder=1 then d.name else '' end,     表说明     = ...

  4. 如何 SQL Server 2005 实例之间传输登录和密码

    INTRODUCTION 本文介绍如何不同服务器上的 Microsoft SQL Server 2005 实例之间传输登录和密码. 本文, 服务器 A 和服务器 B 是不同的服务器. 此外, 服务器 ...

  5. SQL Server 2005下的分页SQL

    其实基本上有三种方法: 1.使用SQL Server 2005中新增的ROW_NUMBER 几种写法分别如下: 1SELECT TOP 20 * FROM (SELECT 2   ROW_NUMBER ...

  6. 如何使用 DBCC MEMORYSTATUS 命令来监视 SQL Server 2005 中的内存使用情况

    https://technet.microsoft.com/en-us/solutionaccelerators/dd537566.aspx 注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完 ...

  7. 数据库开发基本操作-安装Sql Server 2005出现“性能监视器计数器要求”错误解决方法...

    今天在安装SQL Server 2005时,出现"性能监视器计数器要求"错误,因为以前出现过这种错误,得到了解决.今天又又出现这种错误,但并不是很清楚当时的解决办法,所以这次把解决 ...

  8. 数据库开发基本操作-关于sql server 2005 未开放1433端口的问题

    有些sql server 2005在安装过程中,可能将SQL server 服务的端口配置成了动态端口,没有使用默认的1433端口,从而导致了sql server 2005 的服务启动了,但是却没有开 ...

  9. ASP.NET 2.0在SQL Server 2005上自定义分页

    这篇文章讲述了如何利用SQL Server 2005的新特性来简单高效的实现分页.对于那些暂时还没用到SQL Server2005的人们,请看在大规模数据中的高效分页方法.如果需要,这篇文章会补上这里 ...

最新文章

  1. 淘宝特价版给拼多多送芒果,网友:这是什么操作?
  2. 菜鸟学Linux 第052篇笔记 httpd-install and section2
  3. AuthFailed at /social-auth/complete/facebook/
  4. 消防信号二总线有没电压_春晓161#地块人防工程消防电源监控系统的设计与应用...
  5. 1823政府经济学 (2)
  6. python 执行shell_python执行shell命令的方法
  7. emacs .emacs_使用Emacs进行社交并跟踪您的待办事项列表
  8. 将动态路由、布局和RouteViews添加到Blazor应用程序组件
  9. 开源商业模式是万恶之本?
  10. C++-Qt【1】-退出程序静态调试
  11. 百度竞价教程 借助百度热力图让你的效果翻10倍
  12. AI智能电话销售机器人源码搭建部署系统电话机器人源码
  13. mysql jion on 三表_MySQL 三表连接(join)
  14. java excel 插件开发工具_强力推荐!五款能让你成为Excel“高手”的Excel插件
  15. 阻止微信后退,阻止微信页面关闭 history.pushState 无刷新改变页面URL
  16. RabbitMQ与Erlang的版本对应关系
  17. 中科大计算机网络空间安全,2020年中国科学技术大学网络空间安全考研经验分享...
  18. 数控铣削图案及编程_数控铣床编程30例带图
  19. 前端静态页面基本开发思路(一)
  20. [ 人力资源面试篇 ] HR 面试题分析详解大集合,看完直怼面试官(一)

热门文章

  1. OSChina 技术周刊第十六期 —— 每周技术精粹
  2. Java模式(适配器模式)
  3. 使用fswatch工具进行golang的热编译
  4. 关于win2003服务器远程断开后自动注销的问题解决
  5. 开源Wiki系统:XWiki 2.0.2 发布
  6. Python3 与 C# 面向对象之~封装
  7. 如何在CentOS/RHEL 7上借助ssm管理LVM卷?
  8. android----HttpClient的get,post和图片上传服务器
  9. 【MongoDB】chunk too big to move的解决方案
  10. hibernate 持久化