較之前一版本,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這個操作符被置于一個查詢的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獲得産品資訊。
然後我們做如下的查詢:對Production.WorkOrder作查詢,并列出對應的Product的資訊:
下面是查詢結果:

我們可以看到ProductNumber和Name兩個Column實際上是來自TVF中的,其餘才是來自于Production.WorkOrder。如果把TVF看作一個Table,通過查詢結果我們可以看出,上面的查詢相當于把這個Table和Production.WorkOrder通過ProductID作了一個Join。到底是Inner Join,還是Outer Join?我們對這個TVF作如下修改,使其在正常的情況下傳回一個空的結果集(WHERE ProductID = @product_id * -1):
再次運作上面的查詢,我們會發現最終傳回的結果為空:
看來CROSS APPLY使用的是Inner Join。我們現在來試試OUTER APPLY:
下面是最終的輸出結果,我們發現所有的Order記錄被傳回,通過TVF獲得的ProductNumber和Name的值為NULL。這充分說明了OUTER APPLY采用的是OUTER JOIN。
Common Table Expression(CTE)可以看成是一個臨時建立的View,他的生命周期僅僅限于目前Context。一旦CTE被建立,你可以将它當成一般的Table,大部分基于Table的操作都可以運用于CTE。下面是建立CTE的文法結構:
E.G.
CTE具有廣泛的運用,他往往具有将問題化繁為簡的魔力。下面介紹幾個典型的運用:
1、将複雜的Aggregate置于CTE中,将複雜的問題分解為多個步驟。
如果我們現在需要統計每個客戶發出的訂單數量(相關資料存儲于Sales.SalesOrderHeader中),同時輸出客戶的個人資訊(相關資料存儲于Sales.Customer中)。雖然這樣的功能很簡單,但他展現了一種思想,把一部完成略顯複雜的功能程序分解成多個簡單的步驟。
2、使用CTE代替自連接配接,以便更易于了解。
假設我們有一個Product表用于存儲每個Product的資訊,每個Product有一個唯一辨別Product_ID和一個不唯一的Product_Name。由于不同的Product可能重名,倘若我們有這樣的一個需求:需要将重名的記錄(除了具有最小ID的那個)删除,進而保證其名稱的唯一性。我們來看看如何保這些需要上出的記錄篩選出來。Product表的記錄如下,ID為1和4的兩條記錄重名,現在我們的目的是把ID為4的記錄篩選出來。
在不考慮CTE的情況下,我們通過下面的SQL實作這個功能,這個SQL采用了自連接配接。雖然SQL看起來很簡潔,但是相信有一些人第一次看到這樣一個SQL,不能立即了解。
但是如果我們采用了CTE,通過下面一段SQL來實作,雖然代碼多了點,但是從語義上看要易于了解一點:首先把重名的選出來,在和Product作一次連接配接。
3、 用于具有層次結構記錄的遞歸查詢
比如一個公司的員工體系就是一個包含上下級關系的具有階層化的樹形結構。假設我們有如下一個EMPLOYEE表,通過REPORT_TO展現每個員工的上下級關系(假設Empoyee_Name具有唯一性)。
我們現在的需求是:列出員工A的所有下級。為了實作這樣的一個功能,我們需要以一種特殊的結構來建立CTE:
我們發現CTE中主體部分由兩個SELECT語句組成,我們把第一個叫做Anchor Member(AM),AM不會遞歸,隻會執行一次,本例中篩選出了級别最高的A;另一個SELECT語句叫做Recursive Member(RM),RM通過CTE本身和EMPLOYEE表建立連接配接,是以RM會采用遞歸的方式執行。
T-SQL Enhancement in SQL Server 2005:
<a href="http://www.cnblogs.com/artech/archive/2007/08/23/867412.html" target="_blank">[原創]T-SQL Enhancement in SQL Server 2005 - Part I</a>
<a href="http://www.cnblogs.com/artech/archive/2007/08/24/868956.html" target="_blank">[原創]T-SQL Enhancement in SQL Server 2005 - Part II</a>
Reference: 《Programming Microsoft SQL Server 2005》 By Andrew J. Brust & Stephen Forte
作者:蔣金楠
微信公衆賬号:大内老A
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識别二維碼)關注個人公衆号(原來公衆帳号蔣金楠的自媒體将會停用)。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
<a href="http://www.cnblogs.com/artech/archive/2007/08/23/867412.html" target="_blank">原文連結</a>