在 SQLSERVER2005以后,mssql開始有了遞歸查詢的方法了。比較起最開始寫存儲過程或者寫function的方式。這樣的方式更加簡便靈活的。
而oracle也有自帶的樹形結構遞歸查詢方法,connect by
下面我自己寫的一段SQL,簡單注釋下CTE共用表達式的一些用法。 實現對樹狀結構的根節點和子節點的查詢。

-- author:jc_liumangtu(【DBA】小七)
-- date: 2010-03-30 15:09:42
-- version:
-- Microsoft SQL Server 2005 - 9.00.1399.06 (Intel X86)
-- Oct 14 2005 00:33:37
-- Copyright (c) 1988-2005 Microsoft Corporation
-- Developer Edition on Windows NT 5.1 (Build 2600: Service Pack 3)
--
-- ----------------------------------------------------------------------
use test
set nocount on
if object_id ( ' Dept ' , ' U ' ) is not null
drop table Dept
go
create table Dept(ID int ,ParentID int ,Name varchar ( 20 ))
insert into Dept select 1 , 0 , ' AA '
insert into Dept select 2 , 1 , ' BB '
insert into Dept select 3 , 1 , ' CC '
insert into Dept select 4 , 2 , ' DD '
insert into Dept select 5 , 3 , ' EE '
insert into Dept select 6 , 0 , ' FF '
insert into Dept select 7 , 6 , ' GG '
insert into Dept select 8 , 7 , ' HH '
insert into Dept select 9 , 7 , ' II '
insert into Dept select 10 , 7 , ' JJ '
insert into Dept select 11 , 9 , ' KK '
go
SELECT * FROM Dept;
-- 查詢樹狀結構某節點的上級所有根節點。
with cte_root(ID,ParentID,NAME)
as
(
-- 起始條件
select ID,ParentID,NAME
from Dept
where Name = ' II ' -- 列出子節點查詢條件
union all
-- 遞歸條件
select a.ID,a.ParentID,a.NAME
from Dept a
inner join
cte_root b -- 執行遞歸,這里就要理解下了
on a.ID = b.ParentID -- 根據基礎表條件查詢子節點(a.ID),通過CTE遞歸找到其父節點(b.ParentID)。
) -- 可以和下面查詢子節點的cte_child對比。
select * from cte_root ;
-- 查詢樹狀結構某節點下的所有子節點。
with cte_child(ID,ParentID,NAME)
as
(
-- 起始條件
select ID,ParentID,NAME
from Dept
where Name = ' II ' -- 列出父節點查詢條件
union all
-- 遞歸條件
select a.ID,a.ParentID,a.NAME
from Dept a
inner join
cte_child b
on ( a.ParentID = b.ID) -- 根據查詢到的父節點(a.Parent),通過CTE遞歸查詢出其子節點(b.ID)
)
select * from cte_child -- 可以改變之前的查詢條件'II'再測試結果
ID ParentID Name
-- --------- ----------- --------------------
1 0 AA
2 1 BB
3 1 CC
4 2 DD
5 3 EE
6 0 FF
7 6 GG
8 7 HH
9 7 II
10 7 JJ
11 9 KK
ID ParentID NAME
-- --------- ----------- --------------------
9 7 II
7 6 GG
6 0 FF
ID ParentID NAME
-- --------- ----------- --------------------
9 7 II
11 9 KK
在msdn中介紹了CTE的一些限制:
至少有一個定位點成員和一個遞歸成員,當然,你可以定義多個定位點成員和遞歸成員,但所有定位點成員必須在遞歸成員的前面
定位點成員之間必須使用UNION ALL、UNION、INTERSECT、EXCEPT集合運算符,最后一個定位點成員與遞歸成員之間必須使用UNION ALL,遞歸成員之間也必須使用UNION ALL連接
定位點成員和遞歸成員中的字段數量和類型必須完全一致
遞歸成員的FROM子句只能引用一次CTE對象
遞歸成員中不允許出現下列項
SELECT DISTINCT
GROUP BY
HAVING
標量聚合
TOP
LEFT、RIGHT、OUTER JOIN(允許出現 INNER JOIN)
子查詢
接下來介紹下Oracle里面的遞歸查詢方法,connect by prior ,start with。相對於SqlServer來說,Oracle的方法更加簡潔明了,簡單易懂。很容易就讓人理解其用法。借來我會用和上面SqlServer同樣的數據和結構進行代碼演示,和對一些關鍵字的用法進行闡述。
SELECT …..
CONNECT BY {PRIOR 列名1=列名2|列名1=PRIOR 列名2}
[START WITH];
下面是代碼測試:

create table Dept(ID int ,ParentID int ,Name varchar ( 20 ));
-- 增加測試數據,和上面的SqlServer數據相同
insert into Dept select 1 , 0 , ' AA ' from dual;
insert into Dept select 2 , 1 , ' BB ' from dual;
insert into Dept select 3 , 1 , ' CC ' from dual;
insert into Dept select 4 , 2 , ' DD ' from dual;
insert into Dept select 5 , 3 , ' EE ' from dual;
insert into Dept select 6 , 0 , ' FF ' from dual;
insert into Dept select 7 , 6 , ' GG ' from dual;
insert into Dept select 8 , 7 , ' HH ' from dual;
insert into Dept select 9 , 7 , ' II ' from dual;
insert into Dept select 10 , 7 , ' JJ ' from dual;
insert into Dept select 11 , 9 , ' KK ' from dual;
commit ;
-- 查詢根節點(父節點)
select * from Dept -- 查詢基礎表
connect by id = prior parentid -- connect by就是字段的關聯關鍵字,prior有預先和前的意思,則是放在哪個字段前,哪個就是遞歸的上一層
start with name = ' II ' ; -- start with則是遞歸的起始位置,也可以用id或者是parentid。可以修改II的值測試其他數據。
-- 查詢結果
ID PARENTID NAME
9 7 II
7 6 GG
6 0 FF
-- 查詢子節點
select * from Dept
connect by prior id = parentid -- 同樣的語句,僅僅改變prior位子,就發生了指向性的變化,就是這里id為遞歸上一層。
start with name = ' II ' ;
-- 查詢結果
ID PARENTID NAME
9 7 II
11 9 KK
-- 測試結果和SqlServer一致,語句卻更精練,簡潔易懂。
經過分別對SqlServer和Oracle的測試,發現兩個數據庫都很好的支持遞歸查詢,相比之下Oracle的遞歸查詢語句更加簡練易懂,更容易讓人理解。
在做測試的時候,SqlServer更方便的產生測試數據,上面的代碼可以復制后重復執行,而Oracle復制執行一次可以,重復執行的話,在執行創建表的工作,就會報錯了,原因很簡單,Oracle要判斷表存在然后刪除后重建的工作用代碼實現很麻煩。而SqlServer只需要if后drop表再create就搞定。所以兩種數據庫各有千秋。