预排序树实现无限极分类

一.概念

  • 左右值无限级分类,也称为预排序树无限级分类
  • 是一种有序的树状结构
  • 于这些树状结构中的每一个节点都有一个 左值右值

二.规则

  • 每一个 后代节点左值 > 父节点左值
  • 每一个 后代节点右值 < 父节点右值
  • 每一个节点的 右值 < 左值
    预排序树实现无限极分类_第1张图片
    Paste_Image.png

三.新增节点

  • 新增顶级分类:
    • 获取该树中 最大右值;
    • 左值 = 最大右值 + 1;
    • 右值 = 最大右值 + 2;
  • 新增子节点
    • 首先获取 父节点右值
    UPDATE `catagory` SET `lft` = `lft`+2 WHERE `lft`>`父节点右值`
    UPDATE `catagory` SET `rgt` = `rgt` + 2 WHERE `rgt`>= `父节点右值`
    
    • 新增节点左值 = 父节点右值
    • 新增节点右值 = 新增节点左值 + 1

四. 删除节点

  • 获取删除节点的左右值 $lft, $rgt
  • 删除该节点以及所有后代节点
DELETE FROM `catagory` WHERE `lft`>=$lft AND `rgt`<=$Rgt"
  • 更新左右值
$Value=$rgt-$lft+1;
UPDATE `catagory` SET `lft`=`lft`- $Value WHERE `lft`>$lft
UPDATE `catagory` SET `rgt`=`rgt`- $Value WHERE `rgt`>$rgt"

五.数据准备

CREATE TABLE `nested_category` (
  `category_id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `name` varchar(18) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
  `lft` int(4) NOT NULL,
  `rgt` int(4) NOT NULL,
  KEY `category_id` (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `nested_category` VALUES 
(1,'商品',1,26),
(2,'化妆品',2,7),
(3,'食品',8,9),
(4,'酒',10,15),
(5,'服装',16,17),
(6,'家电',18,23),
(7,'鞋帽',24,25),
(8,'面霜',3,4),
(9,'面膜',5,6),
(10,'白酒',11,12),
(11,'红酒',13,14),
(12,'冰箱',19,20),
(13,'空调',21,22);

数据查看
mysql> select * from nested_category;
+-------------+-----------+-----+-----+
| category_id | name      | lft | rgt |
+-------------+-----------+-----+-----+
|           1 | 商品    |   1 |  26 |
|           2 | 化妆品 |   2 |   7 |
|           3 | 食品    |   8 |   9 |
|           4 | 酒       |  10 |  15 |
|           5 | 服装    |  16 |  17 |
|           6 | 家电    |  18 |  23 |
|           7 | 鞋帽    |  24 |  25 |
|           8 | 面霜    |   3 |   4 |
|           9 | 面膜    |   5 |   6 |
|          10 | 白酒    |  11 |  12 |
|          11 | 红酒    |  13 |  14 |
|          12 | 冰箱    |  19 |  20 |
|          13 | 空调    |  21 |  22 |
+-------------+-----------+-----+-----+

六. 获取所有后代节点

select * from nested_category where lft > 18 and rgt < 23;
+-------------+--------+-----+-----+
| category_id | name   | lft | rgt |
+-------------+--------+-----+-----+
|          12 | 冰箱 |  19 |  20 |
|          13 | 空调 |  21 |  22 |
+-------------+--------+-----+-----+
2 rows in set (0.00 sec)

七.计算后代数量

  • 后代的数量 = (右值 - 左值 - 1) / 2。减少1的原因是排除该节点本身

八. 判断叶子节点

  • 右值 - 左值 = 1
  • 获取叶子节点
mysql> select * from nested_category  where rgt - lft = 1;
+-------------+--------+-----+-----+
| category_id | name   | lft | rgt |
+-------------+--------+-----+-----+
|           3 | 食品   |   8 |   9 |
|           5 | 服装   |  16 |  17 |
|           7 | 鞋帽   |  24 |  25 |
|           8 | 面霜   |   3 |   4 |
|           9 | 面膜   |   5 |   6 |
|          10 | 白酒   |  11 |  12 |
|          11 | 红酒   |  13 |  14 |
|          12 | 冰箱   |  19 |  20 |
|          13 | 空调   |  21 |  22 |
+-------------+--------+-----+-----+

九. 检索单一路径

select 
    parent.name,
    parent.category_id, 
    parent.lft,
    parent.rgt
from 
    nested_category as node, nested_category as parent 
where 
    node.lft between parent.lft and parent.rgt and node.name = '空调' 
    order by parent.lft;

+--------+-------------+-----+-----+
| name   | category_id | lft | rgt |
+--------+-------------+-----+-----+
| 商品 |           1 |   1 |  26 |
| 家电 |           6 |  18 |  23 |
| 空调 |          13 |  21 |  22 |
+--------+-------------+-----+-----+
3 rows in set (0.00 sec)

十. 检索分类深度

select 
    node.name as name,  (count(parent.name) - 1) as deep
from 
    nested_category as node,
    nested_category as parent
where node.lft between parent.lft and parent.rgt
group by node.name
order by node.lft
+-----------+------+
| name      | deep |
+-----------+------+
| 商品    |    0 |
| 化妆品 |    1 |
| 面霜    |    2 |
| 面膜    |    2 |
| 食品    |    1 |
| 酒       |    1 |
| 白酒    |    2 |
| 红酒    |    2 |
| 服装    |    1 |
| 家电    |    1 |
| 冰箱    |    2 |
| 空调    |    2 |
| 鞋帽    |    1 |
+-----------+------+
13 rows in set (0.03 sec)

十一. 检索某个节点的子节点(不包含后代节点)

select * from (
    select 
        node.name as name,  
        (count(parent.name) - 1) as deep 
    from 
        nested_category as node,
        nested_category as parent 
    where node.lft between parent.lft and parent.rgt 
    group by node.name 
    order by node.lft
) as a where a.deep <= 1; 
+-----------+------+
| name      | deep |
+-----------+------+
| 商品    |    0 |
| 化妆品 |    1 |
| 食品    |    1 |
| 酒       |    1 |
| 服装    |    1 |
| 家电    |    1 |
| 鞋帽    |    1 |
+-----------+------+
7 rows in set (0.00 sec)

十二.总结

  • 我们看到上边的获取深度检索某个节点的子节点实现上用到了子查询,sql语句很复杂.
  • 所以我的解决办法是在数据结构上增加 深度父id 两个字段
  • 因为分类是前台页面操作人员操作的, 也就是说操作的时候就知道深度, 每次新增时候将 深度父id 带上就可以方便的解决复杂sql语句的问题;

参考文章: http://blog.csdn.net/i_bruce/article/details/41558063

你可能感兴趣的:(预排序树实现无限极分类)