;
with
cte
as
(
select
*
from
tb
where
id=@id
union
all
select
a.*
from
tb a
join
cte b
on
a.id=b.pid
)
select
*
from
cte
公用表表達式(Common Table Expression)是SQL SERVER 2005版本之后引入的一個特性.CTE可以看作是一個臨時的結果集,可以在接下來的一個SELECT,INSERT,UPDATE,DELETE,MERGE語句中被多次引用。使用公用表達式可以讓語句更加清晰簡練.
除此之外,根據微軟對CTE好處的描述,可以歸結為四點:
- 可以定義遞歸公用表表達式(CTE)
- 當不需要將結果集作為視圖被多個地方引用時,CTE可以使其更加簡潔
- GROUP BY語句可以直接作用於子查詢所得的標量列
- 可以在一個語句中多次引用公用表表達式(CTE)
公用表表達式(CTE)的定義
公用表達式的定義非常簡單,只包含三部分:
- 公用表表達式的名字(在WITH之后)
- 所涉及的列名(可選)
- 一個SELECT語句(緊跟AS之后)
在MSDN中的原型:
按照是否遞歸,可以將公用表(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可以更加輕松愉快的用優雅簡潔的方式實現復雜的查詢。