“無限極”分類數據表設計的簡單再總結

 無限級分類實現思路關於該問題,暫時自己還沒有深入研究,在網上找到幾種解決方案,各有優缺點。

 第一種方案:使用遞歸算法,也是使用頻率最多的,大部分開源程序也是這麼處理,不過一般都只用到四級分類。這種算法的數據庫結構設計最爲簡單。category表中一個字段id,一個字段fid(父id)。這樣可以根據WHERE id = fid來判斷上一級內容,運用遞歸至最頂層。分析:通過這種數據庫設計出的無限級,可以說讀取的時候相當費勁,所以大部分的程序最多3-4級分類,這就足以滿足需求,從而一次性讀出所有的數據,再對得到數組或者對象進行遞歸。本身負荷還是沒太大問題。但是如果分類到更多級,那是不可取的辦法。這樣看來這種分類有個好處,就是增刪改的時候輕鬆了…然而就二級分類而言,採用這種算法就應該算最優先了。

第二種方案:設置fid字段類型爲varchar,將父類id都集中在這個字段裏,用符號隔開,比如:1,3,6這樣可以比較容易得到各上級分類的ID,而且在查詢分類下的信息的時候,可以使用:SELECT * FROM category WHERE pid LIKE “1,3%”。 分析:相比於遞歸算法,在讀取數據方面優勢非常大,但是若查找該分類的所有 父分類 或者 子分類 查詢的效率也不是很高,至少也要二次query,從某種意義看上,個人覺得不太符合數據庫範式的設計。倘若遞增到無限級,還需考慮字段是否達到要求,而且在修改分類和轉移分類的時候操作將非常麻煩。 暫時,在自己項目中用的就是類似第二種方案的解決辦法。就該方案在我的項目中存在這樣的問題, 如果當所有數據記錄達到上萬甚至10W以上後,一次性將所以分類,有序分級的現實出來,效率很低。極有可能是項目處理數據代碼效率低帶來的。現在正在改良。

第三種方案:  無限級分類----改進前序遍歷樹 那麼理想中的樹型結構應具備哪些特點呢?數據存儲冗餘小、直觀性強;方便返回整個樹型結構數據;可以很輕鬆的返回某一子樹(方便分層加載);快整獲以某節點的祖譜路徑;插入、刪除、移動節點效率高等等。帶着這些需求我查找了很多資料,發現了一種理想的樹型結構數據存儲及操作算法,改進的前序遍歷樹模型(The Nested Set Model)。 原理: 我們先把樹按照水平方式擺開。從根節點開始(“Food”),然後他的左邊寫上1。然後按照樹的順序(從上到下)給“Fruit”的左邊寫上2。這樣,你沿着樹的邊界走啊走(這就是“遍歷”),然後同時在每個節點的左邊和右邊寫上數字。最後,我們回到了根節點“Food”在右邊寫上18。下面是標上了數字的樹,同時把遍歷的順序用箭頭標出來了。 我們稱這些數字爲左值和右值(如,“Food”的左值是1,右值是18)。正如你所見,這些數字按時了每個節點之間的關係。因爲“Red”有3和6兩個值,所以,它是有擁有1-18值的“Food”節點的後續。同樣的,我們可以推斷所有左值大於2並且右值小於11的節點,都是有2-11的“Fruit” 節點的後續。這樣,樹的結構就通過左值和右值儲存下來了。這種數遍整棵樹算節點的方法叫做“改進前序遍歷樹”算法。 表結構設計: 那麼我們怎樣才能通過一個SQL語句把所有的分類都查詢出來呢,而且要求如果是子類的話前面要打幾個空格以表現是子分類。

要想查詢出所有分類很好辦:SELECT * FROM category WHERE lft>1 AND lft<18 order="" by="" lft="" food="" 18="" fruit="" 11="" food="" fruit="" array_pop="" --="" --="" category="" --="" create="" table="" if="" not="" exists="" category="" id="" int="" 11="" not="" null="" auto_increment="" type="" int="" 11="" not="" null="" comment="" 1="" 2="" 3="" title="" varchar="" 50="" not="" null="" lft="" int="" 11="" not="" null="" rgt="" int="" 11="" not="" null="" lorder="" int="" 11="" not="" null="" comment="" create_time="" int="" 11="" not="" null="" primary="" key="" id="" engine="InnoDB" default="" charset="utf8" auto_increment="10" --="" --="" category="" --="" insert="" into="" category="" id="" type="" title="" lft="" rgt="" lorder="" create_time="" values="" 1="" 1="" 1="" 18="" 1="" 1261964806="" 2="" 1="" 14="" 17="" 50="" 1264586212="" 3="" 1="" 12="" 13="" 50="" 1264586226="" 4="" 2="" 10="" 11="" 50="" 1264586249="" 5="" 1="" 8="" 9="" 50="" 1264586270="" 6="" 3="" 6="" 7="" 50="" 1264586295="" 7="" 1="" 4="" 5="" 50="" 1264586314="" 8="" 1="" 2="" 3="" 50="" 1264586884="" 9="" 1="" 15="" 16="" 50="" 1267771951="" 1="" title="" 2="" 3="" return="" array="" function="" display_tree="" root="" arr_lr="$this-">category->where("title = '頂級欄目'")->find(); //print_r($arr_lr); if($arr_lr){ $right = array(); $arr_tree = $this->category->query("SELECT id, type, title, rgt FROM category WHERE lft >= ". $arr_lr['lft'] ." AND lft <=".$arr_lr['rgt']." ORDER BY lft"); foreach($arr_tree as $v){ if(count($right)){ while ($right[count($right) -1] < $v['rgt']){ array_pop($right); } } $title = $v['title']; if(count($right)){ $title = '|-'.$title; } $arr_list[] = array('id' => $v['id'], 'type' => $type, 'title' => str_repeat('  ', count($right)).$title, 'name' =>$v['title']); $right[] = $v['rgt']; } return $arr_list; } }好了 只要這樣所有的分類都可以一次性查詢出來了,而不用通過遞歸了。下面的問題是怎樣進行插入、刪除和修改操作插入:插入操作很簡單找到其父節點,之後把左值和右值大於父節點左值的節點的左右值加上2,之後再插入本節點,左右值分別爲父節點左值加一和加二,可以用一個存儲過程來操作:CREATE PROCEDURE `category_insert_by_parent`(IN pid INT,IN title VARCHAR(20), IN type INT, IN l_order INT, IN pubtime INT)BEGINDECLARE myLeft INT; SELECT lft into myLeft FROM category WHERE id= pid; UPDATE qy_category SET rgt = rgt + 2 WHERE rgt > myLeft; UPDATE qy_category SET lft = lft + 2 WHERE lft > myLeft; INSERT INTO qy_category(type, title, lft, rgt, lorder, create_time) VALUES(type ,title, myLeft + 1, myLeft + 2, l_order, pubtime); commit;END刪除操作: 刪除的原理:1.得到要刪除節點的左右值,並得到他們的差再加一,@mywidth = @rgt - @lft + 1; 2.刪除左右值在本節點之間的節點 3.修改條件爲大於本節點右值的所有節點,操作爲把他們的左右值都減去@mywidth存儲過程如下:CREATE PROCEDURE `category_delete_by_key`(IN id INT)BEGINSELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM category WHERE id = id;DELETE FROM category WHERE lft BETWEEN @myLeft AND @myRight;UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;修改:要命的修改操作,本人看了很久也沒有看出什麼規律出來,只要出此下策,先刪除再插入,只要調用上面2個存儲過程就可以了!總結:查詢方便,但是增刪改操作有點繁瑣,但是一般分類此類操作不是很多,還是查詢用的多,再說弄個存儲過程也方便!   上面第三種方案具體講解類容是從http://home.phpchina.com/space.php?uid=45095&do=blog&id=184675拷貝過來,方便以後自己查看。 暫時從各方面及理論上考慮 偏向於第三方案。不過還沒有做過測試,到底效率怎麼樣。 期待更好的解決方案!和上邊解決方案一樣,但顯示操作,顯得理詳細http://blog.csdn.net/time_lover/article/details/5828452下邊是另一種解決方案http://www.cnblogs.com/jeffwongishandsome/archive/2010/10/26/1861633.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章