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】数据库设计

相关文章
相关标签/搜索