Common Table Expression,簡稱 CTE,是SQL Server中的三種保存臨時結果的方法之一。另外兩種是臨時表和View,當然你也可以說View並不保存數據,從這一點上來將, CTE更像View一些。
當你的查詢需要從一個源表中統計出結果,基於這個結果再做進一步的統計,如此3次以上的話,你必然會用到View或者臨時表,現在你也可以考慮用CTE了。
CTE的語法相當的簡單, 如下:
With CTE的名字 AS
(
子查詢
)
Select * from CTE的名字
CTE可以實現很多不可思議的功能,巧妙之處在於CTE可以出現自己的子查詢裏。讓我們從簡單的問題開始。
先假設一個需求,貴公司的員工表存放着員工號,員工直接經理的員工號,以及員工的Title,現在需要查詢出各個員工所在的層次,從0開始。
於是你看到這樣的表:
create table Employee
(
MgrId int,
EmpId int,
Title nvarchar(256)
)
表中的內容如下:
NULL | 1 | CEO |
1 | 2 | VP |
2 | 3 | Dev Manager |
2 | 4 | QA Manager |
1 | 5 | Sales Manager |
3 | 30 | Developer |
3 | 31 | Developer |
4 | 40 | Tester |
4 | 41 | Tester |
你期望得到這樣的結果:
NULL | 1 | CEO | 0 |
1 | 2 | VP | 1 |
1 | 5 | SalesManager | 1 |
2 | 3 | DevManager | 2 |
2 | 4 | QAManager | 2 |
4 | 40 | Tester | 3 |
4 | 41 | Tester | 3 |
3 | 30 | Developer | 3 |
3 | 31 | Developer | 3 |
最後一列爲所得到的層次數字。
使用如下的SQL能得到上面的效果:
With DirectReports as
(
select MgrId, EmpId, Title, 0 as [Level] from Employee where MgrId is null
union all
select a.MgrId, a.EmpId, a.Title, [Level]+1 as [Level]
from Employee a join DirectReports b on a.MgrId=b.EmpId
)
select * from DirectReports
爲什麼這個語句能夠沿着CEO往下一層一層走下去,最終找到所有的員工呢?
顯然要理解這一SQL必須理解包含在 as只有括號裏的嵌套查詢。它由兩個查詢結合而成:
select ..
Union All
Select..
這兩個Select語句在CTE中有特殊的意義。
第一個Select子句被稱爲 錨點 語句,它返回的結果跟普通的SQL沒有區別,在這裏返回MgrID爲null的員工。可見沒有Manager是件多麼美好的事情。
第二個子句就沒那麼普通了,它被稱爲 遞歸 語句,請注意到在from後面, Employee和DirectReport進行了鏈接操作。您一定會問,DirectReport的定義還沒完成,這個名字代表什麼結果呢?答案是它不只是代表了一個結果,實際上代表了一系列的結果。換句話說,在DirectReport這個名字下,包含着DirectReport0,DirectReport1,DirectReport2...這些較小的集合。
DirectReport0 是Employee和 錨點 結合的產物;
DirectReport1 是Employee和 DirectReport0 結合的產物;
依次類推, DirectReport n是Employee和DirectReport n-1結合的產物;
當DirectReport_n爲空的時候,這個過程就結束了。
最後 錨點和DirectReport0,DirectReport1... 的並集就是DirectReport的內容。
作爲一個程序員,每次看到遞歸的程序,必然會想到無限遞歸這個錯誤。爲了避免了在開發階段,無限遞歸導致數據庫的崩潰,SQL Server提供了一個QueryHint, MaxRecursion,可以控制遞歸的最大層數,如果超過這個數字而仍爲結束,則視爲代碼錯誤,強制退出。以本文所用的SQL爲例,可以如下使用MaxRecursion。
With DirectReports as
(
select MgrId, EmpId, Title, 0 as [Level] from Employee where MgrId is null
union all
select a.MgrId, a.EmpId, a.Title, [Level]+1 as [Level]
from Employee a join DirectReports b on a.MgrId=b.EmpId
)
select * from DirectReports
Option(MaxRecursion 10)
正如我之前所說, CTE能完成更多的工作,讓我們以後進一步挖掘。