樹形數據在關係數據庫中的存儲同對象一樣,都會遇到一個"阻抗不匹配"的問題。如何設計一個表結構,才能較好的滿足需求呢?
事實上,有很多解決方案,但是沒有哪一種是放之四海而皆準的。我個人認爲解決方案的選擇,必須依賴於需求背景。拋棄需求背景而就技術泛泛而談,就如同孔乙己對回字不同寫法的孜孜追求,滿身酸腐之氣。
凡事有得就有失,十全十美的方案是不存的,合適的就是最好的。
下面就集中常見的方案做一下比對,然後詳細分析一下第四種方案。
方案一:parent_id
Pros:
- 非常容易實現
- 很方便的將子樹移動到另外一個節點
- 添加節點非常簡單
Cons:
- 檢索整棵樹需要使用遞歸,非常耗費時間
- 查找指定節點的所有父(子)節點同樣要使用遞歸,非常耗費時間
- 很容易查找所有父子節點
- 很容易檢索整棵樹
- 添加節點很簡單
- 移動子樹比較麻煩,會導致大量數據更新
- 取決於path的存儲方式,可能需要對path進行解析
- 很容易查找所有父子節點
- 很容易檢索整棵樹
- 添加節點很麻煩,需要同時產生很多關聯關係
- 移動子樹導致大量數據操作
- 數據量可能很龐大
- 很容易查找所有父子節點
- 很容易檢索整棵樹
- 直接使用SQL就可以得到相關的數據
- 存儲方式決定了子節點都是有序存儲的
- 新增,更新,移動都很複雜,每次都會變更非常多的數據
Parent | Title | lft | rgt |
Food | 1 | 18 | |
Food | Fruit | 2 | 11 |
Fruit | Red | 3 | 6 |
Red | Cherry | 4 | 5 |
Fruit | Yellow | 7 | 10 |
Yellow | Banana | 8 | 9 |
Food | Meat | 12 | 17 |
Meat | Beef | 13 | 14 |
Meat | Pork | 15 | 16 |
SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;
2. 查找所有父節點
SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;
select * from tree where lft between 1 and 4 and rgt between 5 and 18 order by lft
update tree set lft = lft + 2 where lft>=11
所有右節點>=11的,都增加2update tree set rgt = rgt + 2 where rgt>=11
3) 新節點放到空位上,左右邊值分別爲11,12INSERT INTO tree SET lft=11, rgt=12, title='Black'
以刪除Food>Fruit>Red 節點爲例子
delete from tree where lft >= 3 and rgt <= 6
update tree set lft = lft - 4 where lft > 6
update tree set rgt = rgt - 4 where rgt> 6
update tree set lft = lft + 9, rht=rgt + 9 where lft >=7 and rgt<=11
update tree set lft = lft - 5 where lft > 11
update tree set rgt = rgt - 5 where rgt> 11