樹形表的平行查詢設計

本文由網友長空X投稿,歡迎轉載、分享

原文作者:長空X(CSDN同名“長空X“,CkTools的作者,github: https://github.com/hjkl950217)

原文鏈接:https://www.cnblogs.com/gtxck/articles/16293295.html

起因

今天在和懶得勤快[1]聊天時談到了樹形表的處理時,發現目前我倆知道的查樹形表都得遞歸查詢,這種方式查詢效率是非常底下且不好維護的,那么有沒有一種又簡單能平行查詢的方式呢?后面我倆還真討論了一種,他快速的修改到他的網站中了。

懶得勤快官網

聲明

文章中的幾個方案是我們的討論結果和一部分網絡資料總結。設計方式千萬種,文章中介紹的設計方式是針對大部分需要樹形表的情況而不代表最優解!最優解已經是集合設計方式人員水平業務情況等因素綜合之后的方案,這篇分享只是加速找到你的最優解

什么是樹形表?

關系型數據庫表中,存放樹形結構的表。例如某個字段需要選擇分類,有一級、二級、...N級,可以這樣設計:

IDPID名字或內容
1
評論1
21評論2
31評論3
43評論4
9242a78985677adb71935f4bf69655d7.png

這樣的數據可以組合成我們大學數據結構中的,用來表達層級關系。這里的Id一般情況下用數字最好,但也有不是數字的情況,這點對選擇方案可能有影響,后面會提到這一點。這種數據結構的實體定義一般如下:

class?CommentEntity
{public?int?ID?{get;set;}public?int?PID?{get;set;}//..?若干數據字段public?CommentEntity?ParentNode?{get;set;}public?List<CommentEntity>?ChildNode?{get;set;}
}

實體定義ParentNode指向父節點,ChildNode指向若干子節點。如果你有數據結構中的鏈表知識,能看出這2個字段起指針域的作用。

數據在數據庫中按存儲,如果我們將數據獲取出來后組裝好ParentNodeChildNode中的指向,然后就能按你的實際業務情況使用了。

有什么用?

有所屬關系的都可以用這種方式存,例如: 權限關系、分類、類型、級別劃分、行政區劃、評論等等等...

但他麻煩之處在于查詢不方便。比如想要查詢一級分類下面的所有數據,按傳統方式需要先查到id=1的一級分類,再查詢PID=1的數據,再查詢PID=剛才查詢的數據ID 這樣遞歸查詢多次直到結束

目標

我們以評論為例

4b7daf6d16407ac9455a227da4d3706a.png

需要滿足:

  1. 進頁面時分頁查詢出主評論,然后按層次關系顯示回評

  2. 可以根據某一個評論查詢下屬所有評論

  3. 平行查詢而不是遞歸查詢

  4. 每個評論數據可以是主評判,也可以是子評論

方案1: 使用tag標記樹

這個方案是添加一個字段tag來標記整顆樹,結構如下:

IDPIDTag內容
1
文章Id1評論1
21文章Id1評論2
31文章Id1評論3
43文章Id1評論4

Tag用于數據庫查詢,ID和PID用于內存中組裝數據,同時對Tag這一列建立非聚集索引。

查詢方式

這里新增的字段在每課樹中都是一樣的,最多查詢2次數據庫即可,然后自己在內存中用Pid重新排列引用關系,修剪掉不需要的數據。

第一次查詢:用評論id查詢出文章id(有文章Id時直接第二步)

第二次查詢:用文章id查詢出所有數據

分頁查詢:查詢后在內存中修剪掉不需要的數據

這種設計基于這些考慮:

  1. Id是數字的情況下,連續的數據大概率在磁盤上是連續存儲,這能提高磁盤IO的效率。如果Id不是數字,用文章Id創建非聚集索引后也能快速查詢。

  2. 在內存中組裝引用關系是非常快的,而且不需要遞歸就能搞定.(遍歷時用PID去查找,找到后直接向ChildNode添加,同時向ParentNode賦值)

  3. 設計邏輯簡單,實習生水平以上的人就能輕松維護這種代碼

缺點:如果一顆評論樹有1000層,那無疑會獲取巨量的無用數據

改進:使用level標記級別

增加級別字段:

IDPIDtaglevel內容
1
文章Id11評論1
21文章Id12評論2
31文章Id12評論3
43文章Id13評論4

查詢時附加上level,能減少一部分無用數據的傳輸,最后復用上面的組裝代碼。

方案2: 使用path標記依賴路徑

借用網上的一張圖直接說明思路(未找到出處,侵權刪除):

deb934c31bf052b1d9a4169b3e529923.png

結合上面說的改造一下:

IDPIDTagPath內容
1
文章Id1
評論1
21文章Id11評論2
31文章Id11評論3
43文章Id11,2評論4

在寫入子節點時需要知道父節點的path,但一般來說這點是能滿足的。Tag和Path用于數據庫查詢,ID和PID用于內存中組裝數據。

查詢方式

查詢全部:仍文章id查詢所有數據,然后在內存中用Pid組裝

查詢id為2及下面的數據:

第一次查詢:查詢id=2的path

第二次查詢:查詢 ?id=2 or startwith $"{path},2"

分頁查詢:

先用文章id按時間排序后查詢前X個,然后進行第2次查詢獲取樓中樓的數據,第2次查詢時可以拼多個 startwith

同時也建議按需冗余level字段以減少查詢,path中雖然隱含了級別數據,但在查詢時并不友好。

這種設計基于這些考慮:

  1. 同方案1差不多,并且理解成本更低

缺點:不算特別的缺點,在查詢子節點數據用path過濾時,是利用不上索引的。

方案3: 不設計樓中樓

借鑒知乎的設計,一看就懂系列:

93c5dfbb1d9687ec840fa9447c2ba0d6.png

知乎的結構中只有評論和回評,回評也只需要保存上一次評論的id即可。這種方式不光設計簡單,閱讀體驗也極好(樓中樓深了并非不好看)

IDPIDGroupIDTag內容
1
1文章Id1評論1
211文章Id1評論2
311文章Id1評論3
431文章Id1評論4
5
2文章Id1評論5

查詢方式

查詢全部:仍文章id查詢所有PID is null的數據,然后在內存中用PID組裝

查詢id為1及下面的數據:查詢 GroupID = 1的數據。這種設計時不會單獨查詢回評的數據

優點:理解成本非常低,同時存儲壓力也小

方案4:使用遞歸

前面不是說不使用遞歸嗎?為什么這里還要提呢?因為:

  1. 有些團隊中有人會固執的認為數據庫不應該返回額外數據,也不應加冗余節點

  2. mysql 8.0 中增加了RECURSIVE來在數據庫層面實現遞歸

  3. 其它無奈

所以如果前面3種方案都不適合你的情況,可能你還得回到遞歸這條路線上面,具體的這里就不提了,網上有許多這類文章。

總結

方案123都是通過冗余字段來降低查詢成本和理解成本,并且利用不同存儲的特性(數據庫不適合運算、內存適合快速讀寫)來實現目標

方案3也是,同時也通過分析優化業務實現技術成本客戶體驗的共贏。

方案4為兜底方案。

我個人比較推崇level+path的組合,這個組合不光能處理評論,也能很好的處理其它的樹形結構,畢竟開發人員不能總是有機會影響業務需求不是?

如果你有更好的方案,歡迎留言討論哦~

參考資料

[1]

懶得勤快: https://masuit.org/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/286987.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286987.shtml
英文地址,請注明出處:http://en.pswp.cn/news/286987.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Ubuntu 16.04安裝SQLite Browser操作SQLite數據庫

安裝&#xff1a; sudo apt-get install sqlitebrowser 啟動&#xff1a;

ArcGIS10從入門到精通系列實驗視頻教程合集(持續更新)

全集視頻請點擊&#xff1a;《ArcGIS10.X從入門到精通系列實驗教程》

(13)python 字典 2 分鐘速解

本系列文章將會以通俗易懂的對話方式進行教學&#xff0c;對話中將涵蓋了新手在學習中的一般問題。此系列將會持續更新&#xff0c;包括別的語言以及實戰都將使用對話的方式進行教學&#xff0c;基礎編程語言教學適用于零基礎小白&#xff0c;之后實戰課程也將會逐步更新。 若…

Kotlin之函數作為參數傳遞

1 、Kotlin之函數作為參數傳遞 我們在寫BaseQuickAdapter適配器的時候,有時候嵌套多個BaseQuickAdapter,如果最里面的view觸發點擊事件,我們可以把函數作為參數通過構造函數一層一層傳遞進去。 2、代碼測試 private var test: (Boolean, Int, Int) -> Unit = {result, a…

計算機繪畫作品 星空,關于近期繪畫作品《星空系列》的自述:

《星空系列》是一個很偶然的念想下產生的&#xff0c;但又絕非偶然。最開初的點是高一剛開始學畫的時候&#xff0c;高中的老師給我們免費提供油畫材料給我們練習&#xff0c;那時候我并沒有“安分”地練習高考繪畫&#xff0c;總想做一些關于自己想法的作品&#xff0c;那時候…

BootStrap導航欄的使用

默認的導航欄 創建一個默認的導航欄的步驟如下&#xff1a; 向 <nav> 標簽添加 class .navbar、.navbar-default。向上面的元素添加 role"navigation"&#xff0c;有助于增加可訪問性。向 <div> 元素添加一個標題 class .navbar-header&#xff0c;內部包…

安裝SQL Server 2005時,出現“SQL Server 服務無法啟動。……”提示的解決方法

安裝SQL Server 2005時&#xff0c;出現“SQL Server 服務無法啟動。……”提示的解決方法上午在自己XP SP3電腦上安裝SQL Server 2005時出現如下提示&#xff1a; 根據這一情況&#xff0c;我把自己的解題步驟寫下來和大家分享&#xff0c;雖然簡單了些&#xff0c;可是安裝的…

.NET MAUI 正式發布,再見了 Xamarin.Forms

David Ortinau 在dotnet 團隊博客上發表了一篇文章《Introducing .NET MAUI – One Codebase, Many Platforms》&#xff0c;在這篇文章里宣布了MAUI的正式發布。https://github.com/dotnet/maui/releases/tag/6.0.312 https://devblogs.microsoft.com/dotnet/introducing-dotn…

南京師范大學湯國安教授《地理信息與人類生活》系列精品課程(5集全)

《地理信息與人類生活》湯國安老師公共課&#xff08;5集全&#xff09; 人類掌握與應用地理信息的歷史&#xff0c;從一個側面反映了人類社會的發展進程。在數字化的時代&#xff0c;地理信息技術更是滲透到人類生活的方方面面。本課程通過五節課程的講解&#xff0c;展示了地…

利用SQL注入獲取服務器最高權限

單位有臺數據庫服務器&#xff08;windows 2000 操作系統&#xff0c;sql server 2000&#xff09;前段時間莫名其妙的被***了跑到機房&#xff0c;通過PE進去一看&#xff0c;發現多了一個賬戶&#xff08;SQLDEBUG)。并且administrator賬戶被禁用了看看數據沒少&#xff0c;也…

3分鐘搞定 C++ if else 語句 05

作者簡介 作者名&#xff1a;1_bit 簡介&#xff1a;CSDN博客專家&#xff0c;2020年博客之星TOP5&#xff0c;藍橋簽約作者。15-16年曾在網上直播&#xff0c;帶領一批程序小白走上程序員之路。歡迎各位小白加我咨詢我相關信息&#xff0c;迷茫的你會找到答案。系列教程將會…

Android之封裝倒計時頁面

1 、需求 多個頁面需要用到顯示定時器頁面,頁面里面時間會一秒一秒減少,頁面布局如下,開了定時器,如果其它頁面也打開會使用之前的頁面里面顯示的時間,也就是說在有效范圍內,時間不刷新。 2 、關鍵點 使用Android自帶的倒計時類CountDownTimer CountDownTimer mCount…

計算機什么的有序集合叫程序,程序是什么有序集合

大家好&#xff0c;我是時間財富網智能客服時間君&#xff0c;上述問題將由我為大家進行解答。程序是為實現特定目標或解決特定問題而用計算機語言編寫的命令有序集合&#xff0c;為進行某活動或過程所規定的途徑。程序&#xff0c;香港和臺灣對英文procedure的中文翻譯&#x…

Javascript中數組去重的六種方法

數組去重 第一種方法&#xff1a;先對數組進行排序sort()&#xff0c;排好序&#xff0c;然后把數組的當前項和后一項進行比較&#xff0c;相同則使用數組的splice(相同的位置&#xff0c;1)&#xff0c;但是為了防止數組塌陷&#xff0c;每次刪除數組元素的時候要把i的值減一。…

GIS宣傳片《地理空間信息革命》視頻全集

《地理空間信息革命》第一集 《地理空間信息革命》第一集&#xff1a;介紹了全球定位系統&#xff08;GPS&#xff09;&#xff0c;地理空間信息系統&#xff08;GIS&#xff09;和數字測繪和地理空間技術的歷史和應用。 《地理空間信息革命》第二集 《地理空間信息革命》第二集…

1小時學會不打代碼制作一個網頁精美簡歷(1)

作者簡介 作者名&#xff1a;1_bit 簡介&#xff1a;CSDN博客專家&#xff0c;2020年博客之星TOP5&#xff0c;藍橋簽約作者。15-16年曾在網上直播&#xff0c;帶領一批程序小白走上程序員之路。歡迎各位小白加我咨詢我相關信息&#xff0c;迷茫的你會找到答案。系列教程將會在…

分部方法 partial

當有如下這樣類似的情況出現的時候&#xff0c;可以有更好的優化方式來處理&#xff0c;那就是分部方法 1 class PartOld2 {3 string name;4 5 public virtual void OnChangeName(string str)6 {7 }8 9 public string Name 10…

Android之解決APP奔潰重啟導致Fragment白屏問題

1、問題 問題APP奔潰重啟導致依附的Fragment白屏問題 2、分析 app奔潰重啟Activity肯定更新了,但是依附在Activity里面的fragment有緩存,用的是以前的activity的content,所以獲取到是空的, 就會導致fragment依附失敗,我們只需要activity不要保存當前fragment就行,直接不…

一文講透為Power Automate for Desktop (PAD) 實現自定義模塊

今天寫了一篇長文&#xff0c;《一文講透為Power Automate for Desktop (PAD) 實現自定義模塊 - 附完整代碼》&#xff0c;有興趣的同學點擊 “閱讀原文” 參考 &#xff0c;文章地址是 https://www.cnblogs.com/chenxizhang/p/16287195.html 微軟的PAD是RPA的一種&#xff0…

Linux學習一天一個命令(2)[cd命令]

Linux cd 命令可以說是Linux中最基本的命令語句&#xff0c;其他的命令語句要進行操作&#xff0c;都是建立在使用 cd 命令上的。所以&#xff0c;學習Linux 常用命令&#xff0c;首先就要學好 cd 命令的使用方法技巧。1. 命令格式&#xff1a;cd [目錄名]2. 命令功能&#xff…