Oracle數(shù)據(jù)庫(kù)遞歸查詢示例詳解
前言
遞歸查詢是 SQL 中非常強(qiáng)大的一種功能,它用于處理具有層次結(jié)構(gòu)或樹(shù)形結(jié)構(gòu)的數(shù)據(jù)。在 Oracle 中,遞歸查詢主要通過(guò) START WITH 和 CONNECT BY 子句實(shí)現(xiàn),這是 Oracle 的傳統(tǒng)方法。從 Oracle 11g Release 2 開(kāi)始,它也支持 ANSI SQL 標(biāo)準(zhǔn)的 WITH 子句(即公共表表達(dá)式 CTE)形式的遞歸查詢。
我將重點(diǎn)介紹最常用、也是 Oracle 特色的 START WITH ... CONNECT BY 語(yǔ)法,并簡(jiǎn)要對(duì)比標(biāo)準(zhǔn)的遞歸 CTE 方法。
一、核心概念與語(yǔ)法 (START WITH … CONNECT BY)
這種語(yǔ)法專門(mén)為處理層次查詢而設(shè)計(jì),非常直觀和高效。
基本語(yǔ)法結(jié)構(gòu):
SELECT [LEVEL], column1, column2, ... FROM table_name [WHERE ...] START WITH condition -- 指定層次結(jié)構(gòu)的根節(jié)點(diǎn)(起點(diǎn)) CONNECT BY [PRIOR] condition -- 定義父節(jié)點(diǎn)和子節(jié)點(diǎn)之間的關(guān)系 [ORDER SIBLINGS BY column_name]; -- 按兄弟節(jié)點(diǎn)排序
關(guān)鍵元素解釋:
LEVEL 偽列:
- 這是一個(gè)系統(tǒng)自動(dòng)生成的偽列,它表示當(dāng)前行在樹(shù)形結(jié)構(gòu)中的層級(jí)。
- 根節(jié)點(diǎn)的
LEVEL為 1,根節(jié)點(diǎn)的直接子節(jié)點(diǎn)為 2,以此類推。
START WITH 子句:
- 用于指定遞歸開(kāi)始的根節(jié)點(diǎn)(一行或幾行)。
- 例如:
START WITH employee_id = 100表示從員工 ID 為 100 的 CEO 開(kāi)始構(gòu)建樹(shù)。
CONNECT BY 子句:
- 這是遞歸查詢的核心,它定義了父行和子行之間的關(guān)系。
- PRIOR 運(yùn)算符:至關(guān)重要。它引用的是父行的列。
CONNECT BY PRIOR child_id = parent_id:表示上一行的child_id等于當(dāng)前行的parent_id。這通常用于從父節(jié)點(diǎn)向下遍歷到子節(jié)點(diǎn)(自上而下)。CONNECT BY child_id = PRIOR parent_id:表示當(dāng)前行的child_id等于上一行的parent_id。這可以用于從子節(jié)點(diǎn)向上遍歷到根節(jié)點(diǎn)(自下而上)。
ORDER SIBLINGS BY 子句:
- 在保持層次結(jié)構(gòu)完整性的前提下,對(duì)同一父節(jié)點(diǎn)下的兄弟節(jié)點(diǎn)進(jìn)行排序。
- 比直接在最后用
ORDER BY更合理,因?yàn)樗粫?huì)打亂樹(shù)的顯示順序。
二、經(jīng)典示例:?jiǎn)T工組織架構(gòu)圖
假設(shè)我們有一個(gè) employees 表,結(jié)構(gòu)如下:
| EMPLOYEE_ID | NAME | MANAGER_ID | JOB_TITLE |
|---|---|---|---|
| 100 | King | (null) | President |
| 101 | Kochhar | 100 | VP |
| 102 | De Haan | 100 | VP |
| 103 | Hunold | 102 | Manager |
| 104 | Ernst | 103 | Analyst |
| … | … | … | … |
需求: 查詢所有員工,并顯示他們的匯報(bào)層級(jí)關(guān)系。
查詢語(yǔ)句(自上而下):
SELECT
LEVEL,
LPAD(' ', (LEVEL-1)*4, ' ') || NAME AS Indented_Name, -- 用縮進(jìn)直觀顯示層級(jí)
EMPLOYEE_ID,
NAME,
MANAGER_ID,
JOB_TITLE
FROM employees
START WITH MANAGER_ID IS NULL -- 從最大的老板開(kāi)始(沒(méi)有經(jīng)理的人)
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID -- 上一行的員工ID = 當(dāng)前行的經(jīng)理ID
ORDER SIBLINGS BY NAME; -- 同一經(jīng)理下的員工按名字排序
查詢結(jié)果可能如下:
| LEVEL | Indented_Name | EMPLOYEE_ID | NAME | MANAGER_ID | JOB_TITLE |
|---|---|---|---|---|---|
| 1 | King | 100 | King | (null) | President |
| 2 | De Haan | 102 | De Haan | 100 | VP |
| 3 | Hunold | 103 | Hunold | 102 | Manager |
| 4 | Ernst | 104 | Ernst | 103 | Analyst |
| 2 | Kochhar | 101 | Kochhar | 100 | VP |
| … | … | … | … | … | … |
從這個(gè)結(jié)果可以清晰地看出 King 是根節(jié)點(diǎn),De Haan 和 Kochhar 向他匯報(bào),Hunold 向 De Haan 匯報(bào),Ernst 向 Hunold 匯報(bào)。
三、其他有用的運(yùn)算符和函數(shù)
CONNECT_BY_ROOT:- 用于獲取當(dāng)前行所在樹(shù)的根節(jié)點(diǎn)的某列值。
SELECT CONNECT_BY_ROOT NAME AS Top_Manager, NAME ...會(huì)為 Ernst 顯示Top_Manager是King。
SYS_CONNECT_BY_PATH:- 顯示從根節(jié)點(diǎn)到當(dāng)前節(jié)點(diǎn)的完整路徑。
SELECT SYS_CONNECT_BY_PATH(NAME, ' -> ') AS Path ...對(duì)于 Ernst,會(huì)顯示-> King -> De Haan -> Hunold -> Ernst。
CONNECT_BY_ISLEAF:- 判斷當(dāng)前行是否是葉子節(jié)點(diǎn)(即沒(méi)有子節(jié)點(diǎn))。是葉子節(jié)點(diǎn)則返回 1,否則返回 0。
四、ANSI SQL 標(biāo)準(zhǔn)方法:遞歸公用表表達(dá)式 (CTE)
Oracle 也支持使用 WITH 子句進(jìn)行遞歸查詢,語(yǔ)法更符合其他數(shù)據(jù)庫(kù)(如 PostgreSQL, SQL Server)的標(biāo)準(zhǔn)。
語(yǔ)法結(jié)構(gòu):
WITH cte_name (column_list) AS (
-- 錨定成員 (Anchor Member):定義根節(jié)點(diǎn)
SELECT column1, column2, ...
FROM table_name
WHERE condition -- 類似于 START WITH
UNION ALL
-- 遞歸成員 (Recursive Member):引用CTE自身,進(jìn)行遞歸join
SELECT t.column1, t.column2, ...
FROM table_name t
JOIN cte_name c ON t.parent_id = c.child_id -- 類似于 CONNECT BY
)
-- 主查詢
SELECT * FROM cte_name;
用遞歸 CTE 實(shí)現(xiàn)上面的例子:
WITH Employee_Tree (LEVEL, EMPLOYEE_ID, NAME, MANAGER_ID, JOB_TITLE) AS (
-- 錨定成員:找到根節(jié)點(diǎn)
SELECT
1 AS LEVEL,
EMPLOYEE_ID,
NAME,
MANAGER_ID,
JOB_TITLE
FROM employees
WHERE MANAGER_ID IS NULL
UNION ALL
-- 遞歸成員:連接員工表和CTE自身
SELECT
p.LEVEL + 1, -- 層級(jí)增加
e.EMPLOYEE_ID,
e.NAME,
e.MANAGER_ID,
e.JOB_TITLE
FROM employees e
INNER JOIN Employee_Tree p ON e.MANAGER_ID = p.EMPLOYEE_ID
)
SELECT * FROM Employee_Tree
ORDER BY LEVEL, NAME;
五、兩種方法的對(duì)比
| 特性 | START WITH ... CONNECT BY (Oracle專用) | 遞歸 CTE WITH (ANSI 標(biāo)準(zhǔn)) |
|---|---|---|
| 語(yǔ)法簡(jiǎn)潔性 | 更簡(jiǎn)潔,專為層次查詢?cè)O(shè)計(jì) | 稍顯冗長(zhǎng),但邏輯清晰 |
| 功能強(qiáng)大性 | 非常強(qiáng)大,有專屬偽列和函數(shù)(LEVEL, SYS_CONNECT_BY_PATH等) | 功能同樣強(qiáng)大,但需要自己實(shí)現(xiàn)類似功能(如用字段記錄Path) |
| 可讀性 | 對(duì)熟悉 Oracle 的人可讀性高 | 遵循聲明式編程,遞歸邏輯更標(biāo)準(zhǔn),對(duì)來(lái)自其他數(shù)據(jù)庫(kù)的用戶可讀性高 |
| 性能 | 通常性能更優(yōu),Oracle 對(duì)其有深度優(yōu)化 | 性能也很好,但可能不如原生語(yǔ)法 |
| 標(biāo)準(zhǔn)性 | Oracle 私有語(yǔ)法 | ANSI SQL 標(biāo)準(zhǔn),可移植性好 |
總結(jié)
- 對(duì)于 Oracle 環(huán)境下的開(kāi)發(fā),START WITH ... CONNECT BY 是處理遞歸查詢的首選,因?yàn)樗Z(yǔ)法簡(jiǎn)潔、功能專一且性能優(yōu)異。
- 如果你需要編寫(xiě)跨數(shù)據(jù)庫(kù)兼容的 SQL,或者希望遞歸邏輯更符合通用的編程思維(先錨定再遞歸),那么應(yīng)該使用遞歸 CTE (
WITH子句)。
無(wú)論是哪種方法,遞歸查詢都是操作樹(shù)形結(jié)構(gòu)數(shù)據(jù)(如組織架構(gòu)、菜單、分類目錄、BOM物料清單)的利器。
到此這篇關(guān)于Oracle遞歸查詢的文章就介紹到這了,更多相關(guān)Oracle遞歸查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ORACLE應(yīng)用經(jīng)驗(yàn)(1)
ORACLE應(yīng)用經(jīng)驗(yàn)(1)...2007-03-03
Oracle中備份表的簡(jiǎn)單sql命令語(yǔ)句
備份表的簡(jiǎn)單sql命令想必有黑多的新手朋友們還是會(huì)忘記的即便是老手也不例外吧,下面與大家分享下具體的sql語(yǔ)句,希望對(duì)你有所幫助2013-05-05
Oracle判斷指定列是否全部為數(shù)字的sql語(yǔ)句
判斷指定列是否全部為數(shù)字在實(shí)際應(yīng)用過(guò)程中很常見(jiàn),下面是具體的示例語(yǔ)句,由此需求的朋友可以借鑒下,希望對(duì)大家有所幫助2013-07-07
Oracle數(shù)據(jù)庫(kù)在windows系統(tǒng)上重啟步驟
有時(shí)候在服務(wù)中重啟了oracle之后,數(shù)據(jù)庫(kù)并不能正常訪問(wèn),下面這篇文章主要介紹了Oracle數(shù)據(jù)庫(kù)在windows系統(tǒng)上重啟的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09
Oracle數(shù)據(jù)庫(kù)中對(duì)null值的排序及mull與空字符串的區(qū)別
這篇文章主要介紹了Oracle數(shù)據(jù)庫(kù)中對(duì)Null值的排序及Null與空字符串的區(qū)別,講解了在order by排序中如何定義null值項(xiàng)的位置及其與字符串' '的不同,需要的朋友可以參考下2016-03-03
淺談Oracle數(shù)據(jù)庫(kù)的建模與設(shè)計(jì)
淺談Oracle數(shù)據(jù)庫(kù)的建模與設(shè)計(jì)...2007-03-03
Oracle數(shù)據(jù)庫(kù)中創(chuàng)建自增主鍵的實(shí)例教程
Oracle的字段自增功能,可以利用創(chuàng)建觸發(fā)器的方式來(lái)實(shí)現(xiàn),接下來(lái)我們就來(lái)看看Oracle數(shù)據(jù)庫(kù)中創(chuàng)建自增主鍵的實(shí)例教程,需要的朋友可以參考下2016-05-05

