mysql無限級分類

第一種方案: 
使用遞歸算法,也是使用頻率最多的,大部分開源程序也是這麼處理,不過通常都只用到四級分類。 這種算法的數據庫結構設計最爲簡單。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」 節點的後續。這樣,樹的結構就經過左值和右值儲存下來了。這種數遍整棵樹算節點的方法叫作「改進前序遍歷樹」算法。 

表結構設計:html

那 麼咱們怎樣才能經過一個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); 

/** 
* 顯示樹,把全部的節點都顯示出來。 
* 一、先獲得根結點的左右值(默認根節點的title爲「頂級目錄」)。 
* 二、查詢左右值在根節點的左右值範圍內的記錄,而且根據左值排序。 
* 三、若是本次記錄右值大於前次記錄的右值則爲子分類,輸出時候加空格。 
* @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) 
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個存儲過程就能夠了! 
總結:查詢方便,可是增刪改操做有點繁瑣,可是通常分類此類操做不是不少,仍是查詢用的多,再說弄個存儲過程也方便! 

   【轉載:https://www.cnblogs.com/dotnet-wyoup/p/5370398.html】數據庫設計

相關文章
相關標籤/搜索