1. 程式人生 > >oracle層次查詢connect by (讀書筆記)

oracle層次查詢connect by (讀書筆記)

--oracle層次查詢connect by 
--首先給scott使用者授查詢許可權:
GRANT SELECT ON employees TO scott;GRANT SELECT ON jobs TO scott;


CREATE TABLE employee AS
SELECT a.employee_id,a.first_name||','||a.last_name employee_name,
       b.job_title,a.manager_id,a.department_id
FROM hr.employees a,hr.jobs b
WHERE a.job_id = b.job_id 
AND a.employee_id < 120;


具體資料層次如下:


/*CONNECT BY 子句說明每行資料將是按層次順序檢索,並規定將表中的資料連入樹形結構的關係中
START WITH子句為可選項,用來標識哪個節點作為查詢樹形結構的根節點,若該子句被忽略,則表示所有滿足查詢條件的行作為根節點
基本語法:
SELECT {欄位1,欄位2...}
[LEVEL|connect_by_root|connect_by_isleaf|connect_by_iscycle]
FROM tablename
[WHERE]
CONNECT BY {PRIOR 列名 1=列名 2|列名 1=PRIOR 列名 2}
[START WITH]
[ORDER [SIBLINGS] BY];


層次查詢是通過start WITH 和connect BY 子句標識的
1)CONNECT BY 
CONNECT BY 子句說明每行資料將是按層次順序檢索,並規定將表中的資料連入樹型結構的關係中,PRIOR運算子必須放置在連線關係的兩列中某一個的前面,
對於節點間的父子關係,prior運算子在一側表示父節點,在另一側表示子節點,從而確定查詢樹形結構的順序是自頂向下還是自底向上,
當然也可以理解為遞迴的當前層資料和上一層資料之間的關係
此外,CONNECT BY 子句也不限定父子關係,比如執行level<5等條件。
2)START WITH
START WITH 子句為可選項,用來標識哪個節點作為查詢樹形結構的根節點。
層次查詢需要確定起始點,通過START WITH 子句,後面加條件,這個條件是任何合法的條件表示式。
START WITH將確定將哪行作為root,如果沒有START WITH,則每行都當做root,然後查詢其後代,這不是一個真實的查詢,START WITH後面可以使用子查詢,
如果有where條件,則會截斷層次中的相關滿足條件的節點,但是不影響整個層次結構。
3)LEVEL
是一個“偽列”,代表當前節點所在的層級,對根節點來說,level返回1,根節點的子節點返回2,以此類推。
通過該偽列結合其他的oracle函式進行資料的格式化顯示,能達到意想不到的結果。
4)connect_by_root
connect_by_root必須與某個欄位搭配使用,目的是獲取根節點記錄的欄位資訊。
5)connect_by_isleaf
判斷當前節點是否為葉子節點,0表示為非葉子節點,1表示為葉子節點(如果不存在下級節點就是葉子節點)。
6)connect_by_iscycle
可以檢查是否再樹形查詢的過程中構成迴圈,這個偽列只是在connect BY NOCYCLE方式下有用。
7)ORDER SIBLINGS BY
定義返回是同一個父親下各兄弟之間的順序。
*/
樣例:

列出所有員工自上而下的組織結構層次,並且按照員工號排序,通過常用的order BY 子句來實現。

SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
START WITH employee_id=100
CONNECT BY PRIOR employee_id=manager_id
ORDER BY emp_id;


這裡顯然是按照員工ID 進行排序,但層級關係看上去有些問題。
列出所有員工自上而下的組織結構層級,先按上級員工號排序再按員工號排序,通過order SIBLINGS BY 子句來實現。
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
START WITH employee_id=100
CONNECT BY PRIOR employee_id=manager_id
ORDER SIBLINGS BY emp_id;


這是一個標準的樹形目錄結構,首先,按照層級關係,再次,按照同一層級下的員工號來排序。

(一)列出employee_id=101的員工自上而下的組織結構層級。
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
START WITH employee_id=101
CONNECT BY PRIOR employee_id=manager_id;


(二)列出employee_id=113的員工自下而上的組織結構層級。
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
START WITH employee_id=113
CONNECT BY employee_id=PRIOR manager_id;


(三)列出employee_id=113的員工的兄弟節點(親兄弟,即同部門員工)
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id
FROM employee
WHERE manager_id = (SELECT manager_id FROM employee WHERE employee_id=113);


(四)列出employee_id=113的員工的同級節點(族兄弟)
WITH tmp AS
(SELECT a.*,LEVEL lev
FROM employee a
START WITH employee_id=100
CONNECT BY PRIOR a.employee_id = a.manager_id)
SELECT * FROM tmp 
WHERE lev = (SELECT lev FROM tmp WHERE employee_id = 113);


(主要考慮到是用自身的level去關聯,所以想到用with寫個臨時表)

(五)加where的基本語法,僅列出部門=100的員工的組織結構層次,大家可以看到level仍然遵循原有的層級關係,而不是從1開始編號,很顯然where
語句是在整個層次查詢結束後,再對結果集進行第二次過濾的
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
WHERE department_id=100
START WITH employee_id=100
CONNECT BY PRIOR employee_id= manager_id;


(六)沒有start WITH的查詢,我們可以先看看效果再做解釋
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL
FROM employee
CONNECT BY PRIOR employee_id= manager_id;


我們可以看到如果沒有start WITH,則每行都當做root,然後查詢其後代,這有點類似於笛卡爾積,顯然這不是一個真實的查詢。
所以在層次查詢中最好從一個起點開始遍歷,因此start with是非常有必要的


level偽列與其他oracle函式rpad,lpad的應用
1)rpad是根據層級在右側填充3*層級的空格
2)LPAD是根據層級在左側填充3*層級的下劃線‘_’
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL,
       RPAD(',',LEVEL*3)||employee_name level_with_rpad,
       LPAD(employee_name,LENGTH(employee_name)+(LEVEL*2)-2,'_') level_with_lpad
FROM employee
START WITH employee_id=100
CONNECT BY PRIOR employee_id= manager_id;






connect_by_isleaf,connect_by_root偽列,該例子列出所有2級節點員工的上級員工,以及其是否為葉子節點
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL,
       connect_by_isleaf,
       connect_by_root employee_name root_emp
FROM employee
WHERE LEVEL=3
START WITH employee_id=100
CONNECT BY PRIOR employee_id= manager_id;




關於connect_by_iscycle的使用,首先需要製造一個迴圈引用的例子,然後再通過nocycle關鍵字和connect_by_iscycle進行判斷
UPDATE employee SET manager_id=107 WHERE employee_id = 102;
--將102的上級改成了107,這樣102的下級有103,繼續下級有107,而把102上級改成107之後,就出現了迴圈
COMMIT;
SELECT employee_id emp_id,employee_name emp_name,job_title,manager_id,department_id dept_id,LEVEL,
       connect_by_iscycle "Cycle"
FROM employee
START WITH employee_id = 102
CONNECT BY NOCYCLE PRIOR employee_id = manager_id;


通過查詢結果能夠清晰的看到cycle=1的107員工的上下級關係可能出現迴圈巢狀,然後對該員工進行相應的維護即可。