SQL Server 2008中的CTE遞歸查詢

今天基本搞清楚SQL Server中遞歸查詢的實現方式,So,先記錄下來。不過呢,我的以爲SQL Server的遞歸查詢相對於Oracle中的遞歸查詢更加難以理解困惑
從SQL Server 2005開始,咱們能夠直接經過CTE來支持遞歸查詢,這對查詢樹形或層次結構的數據頗有用。CTE即公用表表達式,雖然不恰當,但你能夠將它看作成一個臨時命名的結果集合。
咱們先創建一個示例表,名稱爲MENU,表示菜單的層次結構:
sql

CREATE TABLE MENU 
(
	name nvarchar(50) NOT NULL PRIMARY KEY,
	senior nvarchar(50) NULL
);

INSERT INTO MENU values
	('文件',NULL),
	('新建','文件'),
	('項目','新建'),
	('使用當前鏈接查詢','新建');



表示的菜單層次以下:

文件
    新建
        項目
        使用當前鏈接查詢 spa

OK,先看CTE的語法:
  WITH CTE名稱(字段列表)
  AS
  (
    查詢語句
  )
例如:
WITH lmenu(name,senior)
as
(
    SELECT name,senior from menu
) code

咱們定義了一個名稱爲lmenu的CTE,這樣咱們後續便可直接使用lmenu來查詢,如:
SELECT * FROM lmenu 對象

若是咱們在定義CTE的查詢語句中直接引用CTE表自己,則會造成遞歸查詢,固然遞歸查詢具備本身的特殊結構,下面的SQL經過遞歸查詢獲取每一個菜單的層次深度: 遞歸

WITH lmenu(name,senior,level) as
 (
	SELECT NAME,SENIOR,0 level FROM MENU WHERE SENIOR IS NULL
	UNION ALL
	SELECT A.NAME,A.SENIOR,b.level+1 FROM MENU A,lmenu b
	where a.senior = b.name
)
SELECT *  from lmenu



結果:

name                                               senior                                             level
-------------------------------------------------- -------------------------------------------------- -----------
文件                                                 NULL                                               0
新建                                                 文件                                                 1
使用當前鏈接查詢                               新建                                                 2
項目                                                 新建                                                 2 io

 

注意查詢定義語句,它由兩條查詢語句構成,其中 class

SELECT NAME,SENIOR,0 level FROM MENU WHERE SENIOR IS NULL select

稱爲定位成員,SQL Server經過此語句來判斷是否繼續進行遞歸。
語句 循環

SELECT A.NAME,A.SENIOR,b.level+1 FROM MENU A,lmenu b
    where a.senior = b.name 語法

稱之爲遞歸成員,其特徵爲from子句中引用了CTE對象自身。
遞歸CTE具備一些限制條件(引自MSDN):

至少有一個定位點成員和一個遞歸成員,固然,你能夠定義多個定位點成員和遞歸成員,但全部定位點成員必須在遞歸成員的前面
定位點成員之間必須使用UNION ALL、UNION、INTERSECT、EXCEPT集合運算符,最後一個定位點成員與遞歸成員之間必須使用UNION ALL,遞歸成員之間也必須使用UNION ALL鏈接
定位點成員和遞歸成員中的字段數量和類型必須徹底一致
遞歸成員的FROM子句只能引用一次CTE對象
遞歸成員中不容許出現下列項
    SELECT DISTINCT
    GROUP BY
    HAVING
    標量聚合
    TOP
    LEFT、RIGHT、OUTER JOIN(容許出現 INNER JOIN)
    子查詢

CTE遞歸查詢的執行方式:
遞歸的終止依賴於定位點成員的,若是理解了這一點,也就理解了遞歸查詢的執行方式。
咱們來看上例的執行執行過程:

SELECT * FROM lmenu

這條語句進入遞歸查詢

SELECT A.NAME,A.SENIOR,b.level+1 FROM MENU A,lmenu b
    where a.senior = b.name

做爲最外層的語句,顯然遞歸的第一層應該根據MENU表的記錄來循環(若是查詢執行計劃,這表示一個嵌套循環),假設menu表中查詢出的記錄順序以下:

name                                               senior
-------------------------------------------------- --------------------------------------------------
文件                                                 NULL
新建                                                 文件
使用當前鏈接查詢                               新建
項目                                                 新建

 

第一條記錄:
首先判斷是否進入遞歸,因爲 文件包含在定位點成員結果集中,不符合遞歸條件,因此不進入遞歸,直接返回從定位點成員集合中返回記錄:

select name,senior,0 level from menu where senior is null and name='文件'

name                                               senior                                             level
-------------------------------------------------- -------------------------------------------------- -----------
文件                                                 NULL                                               0


第二條記錄:
即NAME = '新建', 定位點成員結果集中沒有該記錄,將進入遞歸:
將當前行的值帶入遞歸成員:

SELECT A.NAME,A.SENIOR,b.level+1 level FROM MENU A,lmenu b
    where a.senior = b.name
        AND a.senior = '文件'
        AND a.name='新建'

 

因爲遞歸的關聯條件是a.senior = b.name,因此b.name='文件',以此條件進入下級遞歸,這實際上就是第一條記錄的狀況,因爲name='文件'符合定位點條件,因此將終止遞 歸,若是咱們用子查詢來替換掉lmenu遞歸成員,第二條記錄的查詢語句實際爲:

SELECT a.name,a.senior,b.level+1 from menu a, (
	select name,senior,0 level  from menu where senior is null and name='文件'
) b
where a.senior=b.name
	and a.senior = '文件'
	and a.name='新建'




name                                               senior                                             level
-------------------------------------------------- -------------------------------------------------- -----------
新建                                                 文件                                                 1

 

第三條記錄:
NAME='使用當前鏈接查詢',一樣不符合定位點條件,將進入遞歸:

SELECT A.NAME,A.SENIOR,b.level+1 FROM MENU A,lmenu b
    where a.senior = b.name
          AND a.senior = '新建'
          AND a.name = '使用當前鏈接查詢'

一樣,代入當前記錄條件,下級遞歸b.name='新建',因爲'新建'還不符合定位點條件,因此還將繼續遞歸,及lmenu b表示子查詢:

select c.name,c.senior,d.level+1 level from menu c,lmenu d
where c.senior = d.name
     and c.name = '新建'
     and c.senior = '文件'

替換成上述語句後,d.name='文件',將再次判斷是否須要繼續遞歸,因爲'文件'符合終止遞歸條件,因此將終止遞歸。
咱們用子查詢表示第三條記錄的遞歸過程以下:

SELECT a.name,a.senior,b.level+1 level FROM menu A,(
	select c.name,c.senior,d.level+1 level from menu c,(
	  select name,senior,0 level from menu where senior is null and name='文件'
) d
where c.senior = d.name
	and c.name = '新建'
	and c.senior = '文件'   

) b
where a.senior = b.name
	and a.senior = '新建'
	and a.name = '使用當前鏈接查詢'




name                                               senior                                             level
-------------------------------------------------- -------------------------------------------------- -----------
使用當前鏈接查詢                                           新建                                                 2

第四條記錄與第三條記錄的遞歸層次徹底同樣。

OK,就是這樣,但願對你們有所幫助微笑

相關文章
相關標籤/搜索