常見的實現方案及區別
1. 鄰接表(Adjacency List)
方案描述:
-
每條記錄存儲一個節點的父節點ID。
-
表結構大致:
id INT PRIMARY KEY, name VARCHAR(...), parent_id INT -- 指向父節點的ID,根節點為NULL或0
優點:
-
結構簡單,直觀,容易維護。
-
插入、刪除單條節點簡單。
缺點:
-
查詢整個樹或任意節點的所有子孫節點比較復雜,需遞歸多次查詢(MySQL 8.0之前不支持遞歸CTE,查詢性能低)。
-
需要遞歸處理。
2. 路徑枚舉(Path Enumeration)
方案描述:
-
每條記錄保存從根節點到當前節點的完整路徑。
-
例如路徑字段
path
存儲為/1/5/9/
。 -
表結構大致:
id INT PRIMARY KEY, name VARCHAR(...), path VARCHAR(...) -- 存儲路徑字符串
優點:
-
查詢所有子孫節點通過
LIKE 'path%'
即可,查詢簡單。 -
不用遞歸,查詢效率較高。
缺點:
-
修改節點路徑(如移動節點)時,需要更新所有子節點路徑,操作復雜且成本高。
-
路徑字段存儲占用空間較大。
3. 嵌套集合模型(Nested Set Model)
方案描述:
-
利用左右值(left, right)表示節點的范圍區間。
-
每個節點有兩個整數值
lft
和rgt
,表示在樹中的先序遍歷位置。 -
表結構大致:
id INT PRIMARY KEY, name VARCHAR(...), lft INT, rgt INT
優點:
-
查詢子孫節點非常快(單次范圍查詢)。
-
適合大量讀,查詢頻繁的場景。
缺點:
-
插入、刪除、移動節點時,需要調整大量節點的
lft
和rgt
值,操作復雜。 -
維護成本高。
4. 閉包表(Closure Table)
方案描述:
-
額外建一個關聯表,存儲節點之間的所有祖先-后代關系。
-
關聯表結構:
ancestor_id INT, descendant_id INT, depth INT -- 距離,0表示自身
優點:
-
查詢任意節點的所有子孫和所有祖先非常方便且性能好。
-
插入刪除只需要維護關聯表,靈活。
缺點:
-
需要額外的存儲空間。
-
維護關聯表的操作相對復雜。
5. MySQL 8.0+ 的遞歸公共表表達式(CTE)
-
MySQL 8.0支持遞歸CTE,鄰接表查詢樹形數據變得更簡單。
-
利用遞歸CTE實現遞歸查詢,無需額外結構。
總結對比
方案 | 查詢復雜度 | 插入/更新復雜度 | 存儲成本 | 適用場景 |
---|---|---|---|---|
鄰接表 | 遞歸查詢,慢 | 簡單 | 低 | 數據結構簡單,更新頻繁 |
路徑枚舉 | 簡單LIKE查詢 | 復雜,需更新路徑 | 中等 | 讀多寫少,結構變化不頻繁 |
嵌套集合 | 簡單范圍查詢 | 復雜,調整左右值 | 低 | 讀多寫少,查詢性能要求高 |
閉包表 | 簡單且靈活 | 維護關聯表復雜 | 高 | 需要頻繁查詢任意層級關系 |
遞歸CTE(MySQL8+) | 簡單遞歸查詢 | 簡單 | 低 | 適合任意層級遞歸查詢,無需額外表 |