在程式開發中,我們常遇到用樹型結構來表示某些資料間的關係,如企業的組織架構、商品的分類、操作欄目等,目前的關係型資料庫都是以二維表的形式記錄儲存資料,而樹型結構的資料如需存入二維表就必須進行Schema設計。最近對此方面比較感興趣,專門做下梳理,如下為常見的樹型結構的資料:
其中最簡單的方法是:Adjacency List(鄰接列表模式)。簡單的說是根據節點之間的繼承關係,顯現的描述某一節點的父節點,從而建立二位的關係表。表結構通常設計為{Node_id,Parent_id},如下圖:
使用連線表的大致程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php // $parent is the parent of the children we want to see // $level is increased when we go deeper into the tree, // used to display a nice indented tree function display_children($parent, $level) { // 獲得一個 父節點 $parent 的所有子節點 $result = mysql_query('SELECT name FROM tree WHERE parent="'.$parent.'";'); // 顯示每個子節點 while ($row = mysql_fetch_array($result)) { // 縮排顯示節點名稱 echo str_repeat(' ',$level).$row['name']."\n"; //再次呼叫這個函式顯示子節點的子節點 display_children($row['name'], $level+1); } } ?> |
對整個結構的根節點(Food)使用這個函式就可以列印出整個多級樹結構,由於Food是根節點它的父節點是空的,所以這樣呼叫: display_children(”,0)。將顯示整個樹的內容。如果你只想顯示整個結構中的一部分,比如說水果部分,就可以這樣呼叫:display_children(‘Fruit’,0);
幾乎使用同樣的方法我們可以知道從根節點到任意節點的路徑。比如 Cherry 的路徑是 ”Food >; Fruit >; Red”。 為了得到這樣的一個路徑我們需要從最深的一級”Cherry”開始, 查詢得到它的父節點”Red”把它新增到路徑中, 然後我們再查詢Red的父節點並把它也新增到路徑中,以此類推直到最高層的”Food”
以下是程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php // $node 是那個最深的節點 function get_path($node) { // 查詢這個節點的父節點 $result = mysql_query('SELECT parent FROM tree '. 'WHERE name="'.$node.'";'); $row = mysql_fetch_array($result); // 用一個陣列儲存路徑 $path = array(); // 如果不是根節點則繼續向上查詢 // (根節點沒有父節點) if ($row['parent']!='') { // the last part of the path to $node, is the name // of the parent of $node $path[] = $row['parent']; // we should add the path to the parent of this node // to the path $path = array_merge(get_path($row['parent']), $path); } // return the path return $path; } ?> |
如果對”Cherry”使用這個函式:print_r(get_path(‘Cherry’)),就會得到這樣的一個陣列了:
1 2 3 4 5 6 |
Array ( [0] =>; Food [1] =>; Fruit [2] =>; Red ) |
這種方案的優點很明顯:結構簡單易懂,由於互相之間的關係只由一個parent_id維護,所以增刪改都是非常容易,只需要改動和他直接相關的記錄就可以。缺點當然也是非常的突出:由於直接地記錄了節點之間的繼承關係,因此對Tree的任何CRUD操作都將是低效的,這主要歸根於頻繁的“遞迴”操作,遞迴過程不斷地訪問資料庫,每次資料庫IO都會有時間開銷。舉個例子,如果想要返回所有水果,也就是水果的所有子孫節點,看似很簡單的操作,就需要用到一堆遞迴。當然,這種方案並非沒有用武之地,在樹的層級比較少的時候就非常實用,在鄰接列表模式的基礎上還可以擴充的是平面表,區別是將節點的level和當前節點的順序也放入表中,比較適合類似評論等場景,具體的表結構類似這樣,這裡就不再深入闡述。
參考連結:
- http://salman-w.blogspot.com/2012/08/php-adjacency-list-hierarchy-tree-traversal.html
- https://packagist.org/search/?tags=adjacency%20list