樹形結構資料儲存方案(四):左右值編碼

發表於2017-09-02

在基於資料庫的一般應用中,查詢的需求總要大於刪除和修改。為了避免對於樹形結構查詢時的“遞迴”過程,基於Tree的前序遍歷設計一種全新的無遞迴查詢、無限分組的左右值編碼方案,來儲存該樹的資料。

Nested-Set

第一次看見這種表結構,相信大部分人都不清楚左值(Lft)和右值(Rgt)是如何計算出來的,而且這種表設計似乎並沒有儲存父子節點的繼承關係。但當你用手指指著表中的數字從1數到18,你應該會發現點什麼吧。對,你手指移動的順序就是對這棵樹進行前序遍歷的順序,如下圖所示。當我們從根節點Food左側開始,標記為1,並沿前序遍歷的方向,依次在遍歷的路徑上標註數字,最後我們回到了根節點Food,並在右邊寫上了18。

lft-rgt

依據此設計,我們可以推斷出所有左值大於2,並且右值小於11的節點都是Fruit的後續節點,整棵樹的結構通過左值和右值儲存了下來。然而,這還不夠,我們的目的是能夠對樹進行CRUD操作,即需要構造出與之配套的相關演算法。按照深度優先,由左到右的原則遍歷整個樹,從1開始給每個節點標註上left值和right值,並將這兩個值存入對應的name之中。

如何查詢?

1、獲取某個節點下的所有子孫節點,以Fruit為例:

2、獲取子孫節點總數

子孫總數 = (右值–左值–1)/2,以Fruit為例,其子孫總數為:(11–2–1)/2 = 4

3、 獲取節點在樹中所處的層數,以Fruit為例:

4、 獲取當前節點所在路徑,以Fruit為例:

在日常的處理中我們經常還會遇到的需要獲取某一個節點的直屬上級、同級、直屬下級。為了更好的描述層級關係,我們可以為Tree建立一個檢視,新增一個層次列,該列數值可以編寫一個自定義函式來計算:

在新增完函式以後,我們建立一個a檢視,新增新的層次列:

5、 獲取當前節點父節點,以Fruit為例:

6、 獲取所有直屬子節點,以Fruit為例:

7、 獲取所有兄弟節點,以Fruit為例:

8、 返回所有葉子節點

如何建立樹?如何新增資料?

上面已經介紹瞭如何檢索結果,那麼如何才能增加新的節點呢?Nested set 最重要是一定要有一個根節點作為所有節點的起點,而且通常這個節點是不被使用的。為了便於控制查詢級別,在建表的時候建議新增parent_id配合之聯結列表方式一起使用。

新增子節點(子節點起始處),以在Food下新增子節點Fruit為例:

如需在末尾追加就需要以下方式進行(以在Red下新增Apple為例):

在節點A後面新增同級節點(以在Yellow後面新增Green為例)

以上討論的新增節點指的都是新增末端節點,即插入的這個節點不是當前已存在節點的父節點。如果需要插入非末端節點要怎麼辦呢?

這個過程可以將流程分為2步,首先新增節點,接下里再將需要的節點移到新增的節點下級。節點移動方法(以將Apple移到Yellow中為例):

刪除節點(包含子節點)

如果需要只刪除該節點,子節點自動上移一級如何處理?

以上為Nested Set的CURD操作,具體在使用時建議結合事務和儲存過程一起使用。本方案的優點時查詢非常的方便,缺點就是每次插入刪除資料涉及到的更新內容太多,如果樹非常大,插入一條資料可能花很長的時間。

參考連結:

相關文章