1 定義:
層次查詢使用樹的遍歷,走遍含樹形結構的數據集合,來獲取樹的層次關係報表的方法
樹形結構的父子關係,你可以控制:
① 遍歷樹的方向,是自上而下,還是自下而上
② 確定層次的開始點(root)的位置
層次查詢語句正是從這兩個方面來確定的,start with確定開始點,connect by確定遍歷的方向
2 語法:
註釋:
① level是僞列,表示等級
② from後面只能是一個表或視圖,對於from是視圖的,那麼這個view不能包含join
③ Where條件限制了查詢返回的行,但是不影響層次關係,屬於將節點截斷,但是這個被截斷的節點的下層child不受影響
④ prior是個形容詞,可放在任何地方
⑤ 徹底剪枝條件應放在connect by;單點剪掉條件應放在where子句。但是,connect by的優先級要高於where,也就是sql引擎先執行connect by
⑥ 在start with中表達式可以有子查詢,但是connect by中不能有子查詢
3 遍歷樹:
㈠ Start with子句
Start with確定將哪行作爲root,如果沒有start with,則每行都當作root,然後查找其後代,這不是一個真實的查詢。Start with後面可以使用子查詢或者任何合法的條件表達式
例子:
- select level,id,manager_id,last_name,title from s_emp
- start with title=(select title from s_emp where manager_id is null)
- connect by prior id=manager_id;
㈡ Connect by子句
Connect by與prior確定一個層次查詢的條件和遍歷的方向(prior確定)
Connect by prior column_1=column_2;
其中prior表示前一個節點的意思,可以在connect by等號的前後,列之前,也可以放到select中的列之前
Connect by也可以帶多個條件,比如 connect by prior id=manager_id and id>10
1. )自頂向下遍歷:
先由根節點,然後遍歷子節點。column_1表示父key,column_2表示子key。即這種情況下:connect by prior 父key=子key表示自頂向下,等同於connect by 子key=prior 父key.
例子:
- select level,employee_id,manager_id,last_name,job_id from s_emp
- start with manager_id=100
- connect by employee_id=prior manager_id;
2. )自底向上遍歷:
先由最底層的子節點,遍歷一直找到根節點。與上面的相反。Connect by之後不能有子查詢,但是可以加其他條件,比如加上and id !=2等。這句話則會截斷樹枝,如果id=2的這個節點下面有很多子孫後代,則全部截斷不顯示。
例子:
- select level,employee_id,manager_id,last_name,job_id from s_emp
- start with manager_id=100
- connect by prior employee_id=manager_id and employee_id<>120;
4 使用level和lpad格式化報表:
Level是層次查詢的一個僞列,如果有level,必須有connect by,start with可以沒有
Lpad是在一個string的左邊添加一定長度的字符,並且滿足中間的參數長度要求,不滿足自動添加
例子:
- select level,employee_id,manager_id,lpad(last_name,length(last_name)+(level*4)-4,'_'),job_id from s_emp
- start with manager_id=100
- connect by prior employee_id=manager_id and employee_id<>120
5 修剪branches:
where子句會將節點刪除,但是其後代不會受到影響,connect by 中加上條件會將滿足條件的整個樹枝包括後代都刪除。要注意,如果是connect by之後加條件正好條件選到根,那麼結果和沒有加一樣
6 實際應用
1)查詢每個等級上節點的數目
- 先查看總共有幾個等級:
- select count(distinct level)
- from s_emp
- start with manager_id is null
- connect by prior employee_id=manager_id
- 要查看每個等級上有多少個節點,只要按等級分組,並統計節點的數目即可,可以這樣寫:
- select level,count(last_name)
- from s_emp
- start with manager_id is null
- connect by prior employee_id=manager_id
- group by level
2)查看等級關係
比如給定一個具體的員工看是否對某個員工有管理權
- select level,a.* from
- s_emp a
- where first_name='Douglas' --被管理的節點
- start with manager_id is null --開始節點,即:根節點
- connect by prior employee_id=manager_id
3)刪除子樹
比如有這樣的需求,現在要裁員,將某個部門的員工包括經理全部裁掉
將id爲2的員工管理的所有員工包括自己刪除
- delete from s_emp where employee_id in(
- select employee_id from
- s_emp a
- start with employee_id=2 --從id=2的員工開始查找其子節點,把整棵樹刪除
- connect by prior employee_id=manager_id)
4)找出每個部門的經理
- select level,a.* from
- s_emp a
- start with manager_id is null
- connect by prior employee_id=manager_id and department_id !=prior department_id;--當前行的dept_id不等於前一行的dept_id,即每個子樹中選最高等級節點
5)查詢一個組織中最高的幾個等級
- select level,a.* from
- s_emp a
- where level <=2 –查找前兩個等級
- start with manager_id is null
- connect by prior employee_id=manager_id and department_id !=prior department_id;
6)合計層次
有兩個需求,一是對一個指定的子樹subtree做累加計算salary,一是將每行都作爲root節點,然後對屬於這個節點的所有子節點累加計算salary。
- 第一種很簡單,求下sum就可以了,語句:
- select sum(salary) from
- s_emp a
- start with id=2—比如從id=2開始
- connect by prior id=manager_id;
-
- 第2個需求,需要用到第1個,對每個root節點求這個樹的累加值,然後內部層次查詢的開始節點從外層查詢獲得。
- select last_name,salary,(
- select sum(salary) from
- s_emp
- start with id=a.id –讓每個節點都成爲root
- connect by prior id=manager_id) sumsalary
- from s_emp a;
7)找出指定層次中的葉子節點
Leaf(葉子)就是沒有子孫的孤立節點。Oracle 10g提供了一個簡單的connect_by_isleaf=1,0表示非葉子節點
- select level,id,manager_id,last_name, title from s_emp
- where connect_by_isleaf=1 –表示查詢葉子節點
- start with manager_id=2
- connect by prior id=manager_id;
7 10g新特性:
① 使用SIBLINGS關鍵字排序
如果使用order by排序會破壞層次,在oracle10g中,增加了siblings關鍵字的排序
語法:order siblings by <expre>
它會保護層次,並且在每個等級中按expre排序
例子:
- select level,
- employee_id,last_name,manager_id
- from s_emp
- start with manager_id is null
- connect by prior employee_id=manager_id
- order siblings by last_name;
② CONNECT_BY_ROOT
Oracle10g新增connect_by_root,用在列名之前表示此行的根節點的相同列名的值
例子:
- select connect_by_root last_name root_last_name, connect_by_root employee_id root_id,
- employee_id,last_name,manager_id
- from s_emp
- start with manager_id is null
- connect by prior employee_id=manager_id