樹概念及使用connect by進行級聯查詢

来源:https://www.cnblogs.com/outmanxiaozhou/archive/2018/12/12/10109128.html
-Advertisement-
Play Games

樹 樹,大家都見過,以這種形式的數據關係,就是樹。下麵看一張圖,瞭解什麼是根節點(樹幹)、節點或分叉、葉(葉節點) connect by 級聯查詢 connect by可以用於級聯查詢,常用於對具有樹狀結構的記錄查詢某一節點的所有子孫節點或所有祖輩節點。 來看一個示例,現假設我們擁有一個菜單表t_m ...


樹,大家都見過,以這種形式的數據關係,就是樹。下麵看一張圖,瞭解什麼是根節點(樹幹)、節點或分叉、葉(葉節點)

 

connect by 級聯查詢

connect by可以用於級聯查詢,常用於對具有樹狀結構的記錄查詢某一節點的所有子孫節點或所有祖輩節點。

來看一個示例,現假設我們擁有一個菜單表t_menu,其中只有三個欄位:id、name和parent_id。它們是具有父子關係的,最頂級的菜單對應的parent_id為0。現假設我們擁有如下記錄:

id

name

parent_id

1

菜單01

0

2

菜單02

0

3

菜單03

0

4

菜單0101

1

5

菜單0102

1

6

菜單0103

1

7

菜單010101

4

8

菜單010201

5

9

菜單010301

6

10

菜單0201

2

11

菜單0202

2

12

菜單020101

10

13

菜單020102

10

14

菜單020103

10

15

菜單0301

3

16

菜單0302

3

17

菜單030201

16

18

菜單030202

16

19

菜單030203

16

 

       如果這個時候我們需要查詢“菜單01”以及其下所有的子孫菜單應該怎麼辦呢?如果使用connect by的話這將會非常簡單,使用如下SQL語句就可以達到對應的效果。

Sql代碼:  select * from t_menu connect by parent_id=prior id start with id=1;  

       connect by是需要跟start with一起使用的。connect by後跟的是連接條件,在connect by後接的條件通常都需要使用關鍵字“prior”,可以簡單的把它理解為上一級,所以上述例子中“connect by parent_id=prior id”就表示連接條件為parent_id等於上級的id,查找到下一級記錄後又會找parent_id等於下一級記錄的id的記錄,而prior對應的最頂層的記錄就是通過start with來確定的,start with後接對應的篩選條件,表示最頂層的記錄是哪些,最頂層的記錄可以有多個,比如我想查找“菜單01”下的子孫菜單,但是不包括“菜單01”本身,那麼我就可以使用如下的SQL語句進行查找,此時“start with parent_id=1”對應的記錄就會有多條。

Sql代碼:  select * from t_menu connect by parent_id=prior id start with parent_id=1;  

       對應的結果為:

id

name

parent_id

4

菜單0101

1

5

菜單0102

1

6

菜單0103

1

7

菜單010101

4

8

菜單010201

5

9

菜單010301

6

 

       此外,如果我們想查找“菜單010101”對應的祖輩菜單也非常簡單,如下SQL就可以實現該功能,即從“菜單010101”的父菜單(對應id為4)開始查找。

Sql代碼:  select * from t_menu connect by id=prior parent_id start with id=4;  

       對應的結果為:

id

name

parent_id

1

菜單01

0

4

菜單0101

1

 

level

       使用connect by時我們可以使用內置的類似於rownum的一個叫level的偽列,該列表示當前記錄相對於start with記錄的一個層級,start with記錄的level為1。如上面的兩條SQL語句,如果加上level的話對應的結果將是這樣的。

Sql代碼:  select level,t.* from t_menu t connect by parent_id=prior id start with parent_id=1;  

       對應的結果為:

level

id

name

parent_id

1

4

菜單0101

1

1

5

菜單0102

1

1

6

菜單0103

1

2

7

菜單010101

4

2

8

菜單010201

5

2

9

菜單010301

6

 

Sql代碼:  select level,t.* from t_menu t connect by id=prior parent_id start with id=4;  

       對應的結果為:

level

id

name

parent_id

2

1

菜單01

0

1

4

菜單0101

1

 

       有了level後,我們就可以對查詢的level做一個限制,比如只查從最頂層開始向下兩級的菜單。

Sql代碼:  select level,t.* from t_menu t where level<3 connect by prior id= parent_id start with parent_id=0;  

       從上述SQL我們可以看到where條件是直接跟在from之後的,使用connect by時我們的where條件不是在connect by之前對數據進行過濾的,而是在connect by之後才對所有的數據進行過濾的,這一點跟使用分組語句group by時是不一樣的,group by是先通過where對需要分組的數據進行過濾後再通過group by來分組的。

 

nocycle和connect_by_iscycle

       如果我們的記錄中存在迴圈的父子關係,則使用connect by進行查詢時會拋出異常,如A->B、B->C、C->A這樣的記錄。解決辦法是在connect by語句後加上“nocycle”,表示不迴圈查詢,如:

Sql代碼:  select * from t_menu connect by nocycle prior id=parent_id start with parent_id=0;  

       使用nocycle後對於A->B、B->C、C->A這樣的記錄會通過查詢B,然後通過B查詢C,再通過C查詢A時發現已經迴圈了,就不再查詢了,即在C這條記錄這裡迴圈了。在對存在迴圈記錄的查詢中我們也可以通過“connect_by_iscycle”找到是哪一條記錄迴圈了,“connect_by_iscycle”也是一個偽列,其必須和nocycle一起使用。偽列“connect_by_iscycle”對應的值有0和1,如果某一條記錄的connect_by_iscycle對應的值為1則表示從該條記錄這裡開始迴圈了。如下是一個使用connect_by_iscycle的示例。

Sql代碼:  select connect_by_iscycle,t.* from t_menu t connect by nocycle prior id=parent_id start with parent_id=0;  

connect_by_isleaf

       connect_by_isleaf也是一個偽列,其表示對應的記錄是否是一個葉子節點,即在進行connect by時不能通過該記錄找到下一條記錄。其對應的值有0和1,0表示非葉子節點,1表示是葉子節點。如我只想找出是葉子節點的菜單時對應的SQL可以這樣寫:

Sql代碼:  select connect_by_isleaf,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with parent_id=0;  

connect_by_root

       connect_by_root表示根節點,即某一條記錄所對應的最頂級的記錄,其用法跟prior類似,後面也需要跟一個欄位名。如下麵示例可以查詢所有葉子節點菜單的最頂級菜單和上級菜單的名稱。

Sql代碼:  

select connect_by_root name as root_name, prior name as prior_name,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with parent_id=0;  

       對應上表的記錄,在上述SQL中查詢出來的結果應該如下所示:

root_name

prior_name

id

name

parent_id

菜單01

菜單0101

7

菜單010101

4

菜單01

菜單0102

8

菜單010201

5

菜單01

菜單0103

9

菜單010301

6

菜單02

菜單02

11

菜單0202

2

菜單02

菜單0201

12

菜單020101

10

菜單02

菜單0201

13

菜單020102

10

菜單02

菜單0201

14

菜單020103

10

菜單03

菜單03

15

菜單0301

3

菜單03

菜單0302

17

菜單030201

16

菜單03

菜單0302

18

菜單030202

16

菜單03

菜單0302

19

菜單030203

16

 

sys_connect_by_path

       sys_connect_by_path(column,delimiter)可以用來展示以指定column和分隔符delimiter表示從根節點到當前節點的路徑。以下SQL用來查詢id為2的菜單下葉子節點的信息,包括以欄位name和分隔符“>”表示的其對應的根節點的路徑。

Sql代碼:  select sys_connect_by_path(name, '>') as connect_path,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with id=2;  

       對應結果如下所示:

connect_path

id

name

parent_id

>菜單02>菜單0202

11

菜單0202

2

>菜單02>菜單0202>菜單020101

12

菜單020101

10

>菜單02>菜單0202>菜單020102

13

菜單020102

10

>菜單02>菜單0202>菜單020103

14

菜單020103

10

 

排序order

       可以使用order by對connect by之後的結果進行排序,此時order by需放在最末端,而不像where篩選那樣直接定義在from之後。如需對connect by之後的結果按id進行排序,則可以使用如下SQL語句:

Sql代碼:  select t.* from t_menu t connect by parent_id=prior id start with parent_id=0 order by id;  

       除了傳統的針對查詢結果的排序外,connect by語句還支持對同一父節點下的子節點進行排序,這是通過order siblings by來定義的。如我們需要查詢id為2的菜單下的所有子孫菜單,然後對具有同一父節點的菜單按id進行倒序排列,則我們的SQL語句可以如下定義:

Sql代碼:  select t.* from t_menu t connect by parent_id=prior id start with id=2 order siblings by id desc;  

       對應的結果會是這樣子:

id

name

parent_id

2

菜單02

0

11

菜單0202

2

10

菜單0201

2

14

菜單020103

10

13

菜單020102

10

12

菜單020101

10

 

       如上表所示,我們可以看到“菜單0201”和“菜單0202”具有相同的父節點“菜單02”,它們按照id進行倒序排列,所有“菜單0202”在“菜單0201”之前,同樣“菜單020101”、“菜單020102”和“菜單020103”具有相同的父節點“菜單0201”,所以它們也是按照id的倒序排列。

一次針對connect by的查詢優化

       有這麼一個需求:表A表示分類,表B表示任務模板,A與B是一對多的關係,每一個任務模板都屬於一個特定的分類,在表B中用欄位a表示所屬的分類。分類存在父子關係,子分類的parent_id對應父分類的id。現假設需要統計id為1的分類及其子分類下存在的任務模板數量。對應SQL如下:

Sql代碼:  select count(1) from B b,(select id from A connect by prior id=parent_id start with id=1) a where a.id=b.a;  

       現假設擁有另外一個表C,其表示任務實例,一個任務模板B可以擁有n個任務實例B,即B跟C之間是一對多的關係。任務實例C通過欄位b關聯任務模板B,另外任務實例C擁有一個欄位status表示任務實例的具體狀態。現假設需要統計id為1的分類及其子分類下各狀態的任務實例數量。對應SQL如下:

Sql代碼:  select c.status,count(1) from B b,(select id from A connect by prior id=parent_id start with id=1) a, C c where a.id=b.a and b.id=c.b group by c.status;  

       在A表數據量1000,B表數據量20000,C表數據量5000,id為1的分類下屬的子孫分類數量為100的情況下第一條SQL的查詢速度可以在0.1秒左右完成,而第二條SQL需要將近10秒才能完成。把查詢id為1的分類下子孫分類的id的SQL語句“selectidfrom A connectbypriorid=parent_id startwithid=1”單獨查詢的速度也可以在0.1秒內完成。通常對於這種數量級別的三表查詢都是可以在0.1秒內完成的,為此心想第二條SQL應該是受了子查詢中connect by的影響。後來決定把分類的子查詢直接作為B的in條件進行查詢,如下所示:

Sql代碼:  select c.status,count(1) from B b, C c where b.a in(select id from A connect by prior id=parent_id start with id=1) and b.id=c.b group by c.status;  

       其查詢效果是一樣的,心想應該還是connect by影響到了,既然單獨使用connect by查詢id為1的分類的子孫分類的id只需要不到0.1秒,那何不在程式裡面先將id為1的分類的子孫分類id查詢出來,再作為B、C聯合查詢的in條件,如:

Sql代碼:  select c.status,count(1) from B b, C c where b.a in(...) and b.id=c.b group by c.status;   

       結果查詢結果也可以在0.1秒內完成。

思考

假設需要求菜單parentId為16的數據到菜單03的路徑,用下麵代碼

       select *,sys_connect_by_path(name,'/') from t_menu where parent_id = 16 start with  id is not null connect by  prior id = parent_id

       得出的是每個節點往下的路徑。

  把prior 放在parent_id 前面,可以得出全路徑,但是不好提取到根的路徑。而且如果資料庫很大, start with 用 is not null  會很慢。

  如果這樣操作:

複製代碼
select * from t_menu t
left join (
select connect_by_root id as id ,sys_connect_by_path(name,'/') path
from t_menu
where id=3
start with (select id from t_menu where parent_id =16)
connect by id =prior parent_id) t1
on t.id=t1.id
複製代碼

用connect_by_root 來取出path的actul id,再和原表左聯。會不會好一點?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.排序概述 2.排序分類 3.WritableComparable案例 這個文件,是大數據-Hadoop生態(12)-Hadoop序列化和源碼追蹤的輸出文件,可以看到,文件根據key,也就是手機號進行了字典排序 欄位含義分別為手機號,上行流量,下行流量,總流量 需求是根據總流量進行排序 Bean對 ...
  • 登錄之後使用如下命令: 還有不知是不是因為mysql版本問題,一開始設置的密碼,使用navicat進行連接測試的時候,報錯: 解決辦法,就是使用如下語句再進行一次密碼修改: ...
  • 遷移主要是通過Navicat工具來實現的。遷移工具的選定在此不討論。 遷移前準備 1.提前通知DBA\SA\BI等,並確認發佈計劃及資料庫遷移方案。 2.梳理出SQL Server DB 中影響業務的Job(遷移的過程中要關閉)、DB LinkServer、相關賬號。 3.模擬數據遷移,評估出相關精 ...
  • 今天自學完了大數據方面生態系統的一些核心技術,在這裡整理一下學過的知識算是複習,也方便初學者參考 ...
  • 一. 複製環境準備 1.1 主庫環境(172.168.18.201) 環境 說明 1.2 從庫環境(172.168.18.203) 環境 說明 二. 複製配置 2.1 主庫環境密碼設置 在201主庫上,找到redis目錄下的redis.conf文件,打開文件找到requirepass,在500行,下 ...
  • 一、TOKEN Token的特點: Token原理: TOKEN的實現: Token用在:登錄校驗。 TOKEN與session對比有更高的安全性,無狀態可擴展性和多平臺跨越的優點。 TOKEN可以完美解決跨站請求偽造,負載均衡,無狀態可擴展性等問題。 二、敏捷開發 敏捷開發是以人為核心,迭代循序漸 ...
  • redis資料庫 redis有以下三個特點 redis支持數據的持久化,可以將記憶體中的數據保存在磁碟中,重啟的時候可以再次載入進行使用 redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲 redis支持數據的備份,即master-s ...
  • 嚴謹轉載--否則追究法律責任 作者 王加鴻 bug 1 解決方案 將這兩個路徑下的文件清空即可 ↑ 解決方案: 暫無(如無法連接,會自動創建連接,但可能長久後會造成連接過多) bug 3 解決方案:初步分析,是埋點數據產生的json格式解析出問題了 後來查看源代碼在org.apache.flume. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...