關於該問題,暫時自己還沒有深入研究,在網上找到幾種解決方案,各有優缺點。
第一種方案:
使用遞迴演算法,也是使用頻率最多的,大部分開源程式也是這麼處理,不過一般都只用到四級分類。這種演算法的資料庫結構設計最為簡單。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,
`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),
(7, 1, '人才招聘', 4, 5, 50, 1264586314),
(9, 1, '總裁', 15, 16, 50, 1267771951);
/*** 顯示樹,把所有的節點都顯示出來。
* 1、先得到根結點的左右值(預設根節點的title為「頂級目錄」)。
* 2、查詢左右值在根節點的左右值範圍內的記錄,並且根據左值排序。
* 3、如果本次記錄右值大於前次記錄的右值則為子分類,輸出時候加空格。
* @return array
**/function display_tree()
}$title = $v['title'];
if(count($right))
$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)
begin
declare 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)
begin
select @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個儲存過程就可以了!
總結:查詢方便,但是增刪改操作有點繁瑣,但是一般分類此類操作不是很多,還是查詢用的多!
出處:
無限級分類實現思路
關於該問題,暫時自己還沒有深入研究,在網上找到幾種解決方案,各有優缺點。第一種方案 使用遞迴演算法,也是使用頻率最多的,大部分開源程式也是這麼處理,不過一般都只用到四級分類。這種演算法的資料庫結構設計最為簡單。category表中乙個欄位id,乙個欄位fid 父id 這樣可以根據where id f...
無限級分類的實現
在我們做 的時候,不管是新聞系統還是產品系統,或者是部落格,論壇等等,都少不了與分類打交道。有時候我們經常說分幾級分類,一般分2 3級,具體視情況而定,但是這樣的做法非常死板,不夠靈活,因為不是任何類別下的分類都有子類,孫類 為了能夠使分類靈活,我們經常採用的是無限級分類。無限級分類主要思路有兩種 ...
無限級分類
相信在實際專案中很多人在做專案的時候都會用到無限級分類,無限級分類說白了就是乙個遞迴,遞迴在我們的專案開發中起到很重要的作用,如 麵包屑導航等。下面我們演示乙個遞迴的案例 area array array id 1,name 安徽 parent 0 array id 2,name 海淀 parent...