用SQL Server 2005 CTE簡化查詢

 

SQL Server 2005引進了一個很有價值的新的Transact-SQL語言組件:一個通用表表達式(Common Table Expression,CTE),它是派生表和視圖的一個便捷的替代。通過使用CTE,我們可以創建一個命名結果集來在SELECT、INSERT、UPDATE和DELETE語句中引用,而無須保存結果集結構的任何元數據。在本文中,我將闡述如何在SQL Server 2005中創建CTE——包括如何使用CTE來創建一個遞歸查詢——並舉幾個例子來說明它們是如何使用的。注意,本文中所有例子都使用SQL Server 2005的AdventureWorks示例數據庫。

 

在SQL Server 2005中創建一個基本CTE

 

我們可以在SELECT、INSERT、UPDATE或DELETE語句之前添加一個WITH子句來構成一個CTE。下面的語法顯示了WITH子句的基本構造和CTE定義:

 


[WITH <CTE_definition> [,...n]]
<SELECT, INSERT, UPDATE, or DELETE statement that
calls the CTEs>
<CTE_definition>::=
CTE_name [(column_name [,...n ])] 
AS
(
CTE_query

 

如上面語句所示,你可以在可選的WITH子句中定義多個CTE。CTE定義包含CTE名稱、CTE字段名稱、AS關鍵字和括號中的CTE查詢。注意,CTE字段名稱的數目必須與CTE查詢返回的字段數目相匹配。另外,如果CTE查詢提供所有字段名稱,那麼字段名稱是可選的。

 

現在我們已經對SQL Server的CTE語法有了基本的瞭解,下面讓我們來看一個CTE定義的例子,以便更好地理解這個語法。下面的例子定義了一個命名爲ProductSold 的CTE,接着在SELECT語句中引用了CTE:

 


WITH ProductSold (ProductID, TotalSold)
AS 
(
SELECT ProductID, SUM(OrderQty) 
FROM Sales.SalesOrderDetail
GROUP BY ProductID

SELECT p.ProductID, p.Name, p.ProductNumber, 
ps.TotalSold
FROM Production.Product AS p
INNER JOIN ProductSold AS ps 
ON p.ProductID = ps.ProductID

 

這裏可以看到,WITH子句在引用CTE的SELECT語句之前。WITH子句的第一行包含了CTE的名稱(ProductSold)以及在CTE 的兩個字段名稱(ProductID 和TotalSold)。接着是AS關鍵字,緊接着是括號中的CTE查詢。這樣,CTE查詢返回了每個產品的銷售總數。

 

當Product表與CTE聯接時,WITH子句後面的SELECT語句根據ProductID,引用了CTE的名稱。這個語句就像調用一個表格或視圖一樣調用CTE。但是,與表格與視圖不同的是,CTE只對WITH子句的語句有效。如果在一個後續語句中引用CTE——而沒有重新定義CTE——就會出錯。

 

使用通用表表達式的其中一個優點是你可以在調用語句中多次引用CTE。比如,下面的語句定義了一個命名爲Employees的CTE,接着在跟在WITH子句的SELECT語句中兩次調用CTE:

 


WITH Employees (EmpID, MgrID, FName, LName, Email) 
AS 
(
SELECT e.EmployeeID, e. ManagerID, 
c.FirstName, c.LastName, c.EmailAddress
FROM HumanResources.Employee e
INNER JOIN Person.Contact c
ON e.ContactID = c.ContactID

SELECT e.FName + ' ' + e.LName AS EmpName, 
e.Email AS EmpEmail, 
m.FName + ' ' + m.LName AS MgrName, 
m.Email AS MgrEmail
FROM Employees e
LEFT OUTER JOIN Employees m
ON e.MgrID = m.EmpID 

 

在這個例子中,SQL Server CTE查詢返回一個員工ID、姓名和郵件地址以及他們的經理ID的列表。然後,跟在WITH子句後的SELECT語句將與CTE聯接來返回經理的姓名和郵件地址。你可以通過使用派生表(子查詢)來獲得相同的結果,但是這就意味着需要多次重複相同的子查詢,同時也使代碼更加複雜。

在WITH子句中創建多個CTE

 

在CTE語法中可以看到,我們可以在WITH子句中定義多個CTE,然後在接下來的語句中按照需要多次調用這些CTE。下面的例子說明了這是如何實現的。下面的WITH子句包含了兩個CTE定義:

 


WITH 
Cost (ProductID, AvgCost) 
AS
(
SELECT ProductID, AVG(StandardCost) 
FROM Production.ProductCostHistory
GROUP BY ProductID
), 
Sold (ProductID, AvgSold) 
AS
(
SELECT ProductID, AVG(OrderQty) 
FROM Sales.SalesOrderDetail
GROUP BY ProductID

SELECT p.ProductID, p.Name, 
(AvgCost * AvgSold)AS TotalCost
FROM Sold s
INNER JOIN Production.Product p
ON s.ProductID = p.ProductID
INNER JOIN Cost c
ON p.ProductID = c.ProductID 

 

創建一個遞歸通用表表達式

 

SQL Server中CTE最有價值的功能是創建遞歸查詢的功能——一種反覆自身引用以返回數據子集的查詢類型。遞歸查詢最常用於返回層次式數據。比如,在AdventureWorks數據庫中的Employee表包含了每個員工的經理ID。事實上,經理ID是該經理管理的員工ID。因此, Employee表格包含了從CEO往下的整個管理層次報告結構。

 

可以通過創建一個CTE查詢來定義一個CTE來檢索這個分層結構,其中這個查詢使用一個UNION ALL、UNION、INTERSECT或EXCEPT操作符來聯接多個SELECT語句。下面讓我們來看個例子。

 

在這個WITH子句中,CTE查詢包含兩個用UNION ALL操作符聯接的SELECT語句:

 


WITH Reports (EmpLevel, EmpID, ContactID, MgrID) 
AS
(
SELECT 1, EmployeeID, ContactID, ManagerID
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT r.EmpLevel + 1, e.EmployeeID, e.ContactID, 
e.ManagerID
FROM HumanResources.Employee e
INNER JOIN Reports r
ON e.ManagerID = r.EmpID

SELECT r.EmpLevel, r.EmpID, 
c.FirstName + ' ' + c.LastName AS EmpName, 
MgrID
FROM Reports r
INNER JOIN Person.Contact c
ON r.ContactID = c.ContactID 
ORDER BY r.EmpLevel, r.MgrID, r.EmpID

 

在CTE查詢中的第一個SELECT語句僅僅檢索頂層員工——CEO。爲了檢索到頂層員工,可以使用一個指定ManagerID值爲NULL的WHERE子句實現。

 

換言之,這個人選並不直接向另一個經理報告。注意:SELECT語句中的第一個字段是1。它用於標識這個查詢返回的員工是在最高層,第一層。

 

第二個SELECT語句(在UNION ALL 操作符之後)基於經理和員工ID將Employee表格與Reports CTE聯接。通過這種方式的自引用,SQL Server自動將其作爲遞歸查詢並按照需要重複檢索以返回每個層次的員工。每次查詢運行時,第一個字段值設爲1,然後每層都遞增1。

 

在WITH子句後的SELECT語句將Reports CTE與Contact表聯接來檢索員工的姓名。下面的查詢結果顯示了這個語句返回的數據樣本:

 

EmpLevel

EmpID

EmpName

MgrID

1

109

Ken Sanchez

NULL

2

6

David Bradley

109

2

12

Terri Duffy

109

2

42

Jean Trenary

109

2

140

Laura Norman

109

2

148

James Hamilton

109

2

273

Brian Welcker

109

3

2

Kevin Brown

6

3

46

Sariya Harnpadoungsataya

6

3

106

Mary Gibson

6

3

119

Jill Williams

6

3

203

Terry Eminhizer

6

3

269

Wanida Benshoof

6

3

271

John Wood

6

3

272

Mary Dempsey

6

3

3

Roberto Tamburello

12

3

66

Janaina Bueno

42

3

102

Dan Bacon

42

3

117

François Ajenstat

42

3

128

Dan Wilson

42

3

149

Ramesh Meyyappan

42

3

150

Stephanie Conroy

42

3

176

Karen Berg

42

3

30

Paula Barreto de Mattos

140

3

71

Wendy Kahn

140

3

103

David Barber

140

3

139

David Liu

140

3

21

Peter Krebs

148

3

44

A. Scott Wright

148

3

200

Hazem Abolrous

148

3

218

Gary Altman

148

3

268

Stephen Jiang

273

3

284

Amy Alberts

273

3

288

Syed Abbas

273

4

4

Rob Walters

3

4

9

Gail Erickson

3

4

11

Jossef Goldberg

3

 

結果是根據員工的級別排列的。注意,第二行到第七行顯示的是MgrID值爲109的數據,它是第一行顯示的頂層員工的ID。下面的行反映了相同分層的數據。

 

遞歸CTE,與SQL Server中的其它通用表表達式一樣,提供了強大的數據檢索功能。與視圖不同的是,它並不需要保存元數據。與派生表不同的是,它並不需要重複不必要的代碼。CTE可以將代碼分成不相關的單元,這有助於簡化代碼複雜性。對於遞歸查詢,CTE則更加好用。剛開始使用CTE時,你可能必須花點時間來適應它們,但是一旦熟悉了,那麼你將樂在其中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章