ClosureTable實現高效無限分類

ClosureTable直譯過來叫閉包表?不過不重要,ClosureTable以一張表存儲節點之間的關係、其中包含了任何兩個有關係(上下級)節點的關聯信息sql

ClosureTable演示
ClosureTable實現高效無限分類數據庫

定義關係表CategoryTree,其包含3個字段:閉包

ancestor 祖先:上級節點的id
descendant 子代:下級節點的id
distance 距離:子代到祖先中間隔了幾級ide

這三個字段的組合是惟一的,由於在樹中,一條路徑能夠標識一個節點,因此能夠直接把它們的組合做爲主鍵。以圖爲例,節點6到它上一級的節點(節點4)距離爲1在數據庫中存儲爲ancestor=4,descendant=6,distance=1,到上兩級的節點(節點1)距離爲2,因而有ancestor=1,descendant=6,distance=2,到根節點的距離爲3也是如此,最後還要包含一個到本身的鏈接,固然距離就是0了。code

這樣一來,不盡表中包含了全部的路徑信息,還在帶上了路徑中每一個節點的位置(距離),對於樹結構經常使用的查詢都可以很方便的處理。下面看看如何用用它來實現咱們的需求。
4.1 子節點查詢對象

查詢id爲5的節點的直屬子節點:blog

SELECT descendant FROM CategoryTree WHERE ancestor=5 AND distance=1

查詢全部子節點:排序

SELECT descendant FROM CategoryTree WHERE ancestor=5 AND distance>0

查詢某個上級節點的子節點,換句話說就是查詢具備指定上級節點的節點,也就是ancestor字段等於上級節點id便可,第二個距離distance決定了查詢的對象是由上級往下那一層的,等於1就是往下一層(直屬子節點),大於0就是全部子節點。這兩個查詢都是一句完成。
4.2 路徑查詢遞歸

查詢由根節點到id爲10的節點之間的全部節點,按照層級由小到大排序it

SELECT ancestor FROM CategoryTree WHERE descendant=10 ORDER BY distance DESC

查詢id爲10的節點(含)到id爲3的節點(不含)之間的全部節點,按照層級由小到大排序

SELECT ancestor FROM CategoryTree WHERE descendant=10 AND 
distance<(SELECT distance FROM CategoryTree WHERE descendant=10 AND ancestor=3) 
ORDER BY distance DESC

查詢路徑,只須要知道descendant便可,由於descendant字段所在的行就是記錄這個節點與其上級節點的關係。若是要過濾掉一些則能夠限制distance的值。
4.3 查詢節點所在的層級(深度)

查詢id爲5的節點是第幾級的

SELECT distance FROM CategoryTree WHERE descendant=5 AND ancestor=0

查詢id爲5的節點是id爲10的節點往下第幾級

SELECT distance FROM CategoryTree WHERE descendant=5 AND ancestor=10

查詢層級(深度)很是簡單,由於distance字段就是。直接以上下級的節點id爲條件,查詢距離便可。
4.4 查詢某一層的全部節點

查詢全部第三層的節點

SELECT descendant FROM CategoryTree WHERE ancestor=0 AND distance=3

這個就不詳細說了,很是簡單。
4.5 插入

插入和移動就不是那麼方便了,當一個節點插入到某個父節點下方時,它將具備與父節點類似的路徑,而後再加上一個自身鏈接便可。

因此插入操做須要兩條語句,第一條複製父節點的全部記錄,並把這些記錄的distance加一,由於子節點到每一個上級節點的距離都比它的父節點多一。固然descendant也要改爲本身的。

例如把id爲10的節點插入到id爲5的節點下方(這裏用了Mysql的方言)

INSERT INTO CategoryTree(ancestor,descendant,distance) (SELECT ancestor,10,distance+1 FROM CategoryTree WHERE descendant=5)

而後就是加入自身鏈接的記錄。

INSERT INTO CategoryTree(ancestor,descendant,distance) VALUES(10,10,0)

4.6 移動

節點的移動沒有很好的解決方法,由於新位置所在的深度、路徑均可能不同,這就致使移動操做不是僅靠UPDATE語句能完成的,這裏選擇刪除+插入實現移動。

另外,在有子樹的狀況下,上級節點的移動還將致使下級節點的路徑改變,因此移動上級節點以後還須要修復下級節點的記錄,這就須要遞歸全部下級節點。

刪除id=5節點的全部記錄

DELETE FROM CategoryTree WHERE descendant=5

而後配合上面一節的插入操做實現移動。具體的實現直接上代碼吧。

相關文章
相關標籤/搜索