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” 节点的后续。这样,树的结构就通过左值和右值储存下来了。这种数遍整棵树算节点的方法叫做“改进前序遍历树”算法。
表结构设计:
那 么我们怎样才能通过一个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,之后再插入本节点,左右值分别为父节点左值加一和加二,可以用一个存储过程来操作:
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
存储过程如下:
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】
mysql无限级分类的更多相关文章
- PHP+MySQL无限级分类(非递归)
要实现无限级分类,递归一般是第一个也是最容易想到的,但是递归一般被认为占用资源的方法,所以很多系统是不考虑使用递归的 本文还是通过数据库的设计,用一句sql语句实现 数据库字段大概如下: 字段 说明 ...
- 转:php+mysql菜单无限级分类(非递归)
php+mysql无限级分类(非递归) 参考:http://www.chhua.com/web-note3244
- MySQL技巧(二)——无限级分类表设计
无限级分类表的设计(掌握'自身连接') 类似图书这种,会有很多种分类,而且在现实生活中这种分类会无限的往下分,所以不可能每有一个分类就创建一个分类表.应该使用下面这种语句 DROP TABLE IF ...
- (实用篇)PHP递归实现无限级分类
在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类. 在一些复杂的系统中,要求对信 ...
- PHP无限级分类-递归(不推荐)
[http://www.helloweba.com/view-blog-204.html] 在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢? ...
- PHP递归实现无限级分类
在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类. 在一些复杂的系统中,要求对信 ...
- FreeSql 使用 ToTreeList/AsTreeCte 查询无限级分类表
关于无限级分类 第一种方案: 使用递归算法,也是使用频率最多的,大部分开源程序也是这么处理,不过一般都只用到四级分类. 这种算法的数据库结构设计最为简单.category表中一个字段id,一个字段fi ...
- PHP无限级分类的实现(不使用递归)
无限级分类在开发中经常使用,例如:部门结构.文章分类.无限级分类的难点在于“输出”和“查询”,例如 将文章分类输出为<ul>列表形式: 查找分类A下面所有分类包含的文章. 1.实现原理 在 ...
- 一道无限级分类题的 PHP 实现
今天有网友出了道题: 给出如下的父子结构(你可以用你所用语言的类似结构来描述,第一列是父,第二列是子),将其梳理成类似如图的层次父子结构. origin = [('A112', 'A1122'), ( ...
随机推荐
- Rocket - debug - Periphery
https://mp.weixin.qq.com/s/uGxn-Xec0LkwdaSsCtQBvw 简单介绍Periphery的实现. 1. ExportDebugDMI/ExportDebugJTA ...
- xss(跨站脚本攻击)
xss(跨站脚本攻击) 原理:攻击者可以通过在页面中注入恶意链接或者脚本代码,当受害者访问时,脚本代码会在其浏览器中执行,这个时候,我们可以获取当前用户的cookie或者进行重定向等操作. xss造成 ...
- Java实现 LeetCode 377 组合总和 Ⅳ
377. 组合总和 Ⅳ 给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数. 示例: nums = [1, 2, 3] target = 4 所有可能的组合为: (1, 1 ...
- Java实现 LeetCode 162 寻找峰值
162. 寻找峰值 峰值元素是指其值大于左右相邻值的元素. 给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引. 数组可能包含多个峰值,在这种情况下,返 ...
- Java实现 LeetCode 84 柱状图中最大得矩形
84. 柱状图中最大的矩形 给定 n 个非负整数,用来表示柱状图中各个柱子的高度.每个柱子彼此相邻,且宽度为 1 . 求在该柱状图中,能够勾勒出来的矩形的最大面积. 以上是柱状图的示例,其中每个柱子的 ...
- Java实现 LeetCode 71 简化路径
71. 简化路径 以 Unix 风格给出一个文件的绝对路径,你需要简化它.或者换句话说,将其转换为规范路径. 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身:此外,两个点 (-) 表示将 ...
- java实现 洛谷 P1018 乘积最大
import java.math.BigInteger; import java.util.Scanner; public class Main { private static Scanner ci ...
- 权限系统设计(0):权限系统设计基本概念改需-MAC/RBAC引子
此篇主要对权限系统设计所涉的一些专业术语重点梳理.从我们windows的文件系统 自主访问控制 到基于角色访问控制. 权限设计基本术语 对后面会用到的词汇做一个简要说明 什么是权限(许可) 权限(Pr ...
- 使用 UniApp 实现小程序的微信登录
微信登录思路: 在main.js 中封装公共函数,用于判断用户是否登录 在main.js 中分定义全局变量,用于存储接口地址 如果没有登录.则跳转至登录页面 进入登录页面 通过 wx.login 获取 ...
- AWS—Xshell工具远程登录EC2虚拟机
一.创建AWS账户(已有AWS账号的请跳过) 访问 https://amazonaws-china.com/cn/,点击创建AWS账户,根据提示填写个人相关信息,创建AWS账户. 二.创建EC2虚拟机 ...