T-SQL查詢進階--詳解公用表表達式(CTE)
簡介
對於SELECT查詢語句來說,通常情況下,為了使T-SQL程式碼更加簡潔和可讀,在一個查詢中引用另外的結果集都是通過檢視而不是子查詢來進行分解的.但是,檢視是作為系統物件存在資料庫中,那對於結果集僅僅需要在儲存過程或是使用者自定義函式中使用一次的時候,使用檢視就顯得有些奢侈了.
公用表表達式(Common Table Expression)是SQL SERVER 2005版本之後引入的一個特性.CTE可以看作是一個臨時的結果集,可以在接下來的一個SELECT,INSERT,UPDATE,DELETE,MERGE語句中被多次引用。使用公用表示式可以讓語句更加清晰簡練.
除此之外,根據微軟對CTE好處的描述,可以歸結為四點:
- 可以定義遞迴公用表表達式(CTE)
- 當不需要將結果集作為檢視被多個地方引用時,CTE可以使其更加簡潔
- GROUP BY語句可以直接作用於子查詢所得的標量列
- 可以在一個語句中多次引用公用表表達式(CTE)
公用表表達式(CTE)的定義
公用表示式的定義非常簡單,只包含三部分:
- 公用表表達式的名字(在WITH之後)
- 所涉及的列名(可選)
- 一個SELECT語句(緊跟AS之後)
在MSDN中的原型:
WITH expression_name [ ( column_name [,...n] ) ]AS ( CTE_query_definition )
按照是否遞迴,可以將公用表(CTE)表示式分為遞迴公用表表達式和非遞迴公用表表達式.
非遞迴公用表表達式(CTE)
非遞迴公用表表達式(CTE)是查詢結果僅僅一次性返回一個結果集用於外部查詢呼叫。並不在其定義的語句中呼叫其自身的CTE
比如一個簡單的非遞迴公用表表達式:
當然,公用表表達式的好處之一是可以在接下來一條語句中多次引用:
前面我一直強調“在接下來的一條語句中”,意味著只能接下來一條使用:
由於CTE只能在接下來一條語句中使用,因此,當需要接下來的一條語句中引用多個CTE時,可以定義多個,中間用逗號分隔:
遞迴公用表表達式(CTE)
遞迴公用表表達式很像派生表(Derived Tables ),指的是在CTE內的語句中呼叫其自身的CTE.與派生表不同的是,CTE可以在一次定義多次進行派生遞迴.對於遞迴的概念,是指一個函式或是過程直接或者間接的呼叫其自身,遞迴的簡單概念圖如下:
遞迴在C語言中實現的一個典型例子是斐波那契數列:
long fib(int n) { if (n==0) return 0; if (n==1) return 1; if (n>1) return fib(n-1)+fib(n-2); }
上面C語言程式碼可以看到,要構成遞迴函式,需要兩部分。第一部分是基礎部分,返回固定值,也就是告訴程式何時開始遞迴。第二部分是迴圈部分,是函式或過程直接或者間接呼叫自身進行遞迴.
對於遞迴公用表示式來說,實現原理也是相同的,同樣需要在語句中定義兩部分:
- 基本語句
- 遞迴語句
在SQL這兩部分通過UNION ALL連線結果集進行返回:
比如:在AdventureWork中,我想知道每個員工所處的層級,0是最高階
這麼複雜的查詢通過遞迴CTE變得如此優雅和簡潔.這也是CTE最強大的地方.
當然,越強大的力量,就需要被約束.如果使用不當的話,遞迴CTE可能會出現無限遞迴。從而大量消耗SQL Server的伺服器資源.因此,SQL Server提供了OPTION選項,可以設定最大的遞迴次數:
還是上面那個語句,限制了遞迴次數:
所提示的訊息:
這個最大遞迴次數往往是根據資料所代表的具體業務相關的,比如這裡,假設公司層級最多隻有2層.
總結
CTE是一種十分優雅的存在。CTE所帶來最大的好處是程式碼可讀性的提升,這是良好程式碼的必須品質之一。使用遞迴CTE可以更加輕鬆愉快的用優雅簡潔的方式實現複雜的查詢。