- 浏览: 830895 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
xieye:
jetty插件有好几个版本,1.6,1.7,1.8
我选的是用 ...
tapestry入门(翻译)3 导入项目到eclipse -
xieye:
还有,
注:第2部分时,需要先安装jetty,我自己在安装过程 ...
tapestry入门(翻译)3 导入项目到eclipse -
xieye:
说明一下:实际使用中,导入时我并没有错误。2、我把eclips ...
tapestry入门(翻译)3 导入项目到eclipse -
xieye:
其实还是有一些先决条件的。1是外部环境,2是进步是阶段性的(意 ...
(转载文章)如何愉悦起来:一位精神治疗师的见解 -
mandy_yanzi:
我都已经饿7天了坚持为了我的衣衣
身体健康的问题
原文:
http://be-evil.org/post-168.html
本文是我学习MySQL官方教程Managing Hierarchical Data in MySQL的笔记
多层数据结构估计所有的web开发者估计都不会陌生,各种软件的分类都是基于多层结构来设计的。
下面是一个典型的多层数据结构示意图:
点击查看原图
相关创建数据语句:
CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);
INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);
SELECT * FROM category ORDER BY category_id;
在这种数据结构中,各层之间通过字段 parent 来形成邻接表,我们查询某些层级的关系的时候一般都是通过递归的方式,遍历某个层级关系的SQL的查询次数会顺着层级的增加,想想在层级有20的时候,根据某个底层节点取它到顶层节点的查询次数吧。
为了解决这个问题,人们想出了嵌套集模型(The Nested Set Model),请看下图:
点击查看原图
上图依然是表现的与图一相同的层级关系,但是却更换了一种表现形式 下面是新的关系表和数据(关系和数据与之前相同,但是表结构不一样):
CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
INSERT INTO nested_category
VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),
(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);
SELECT * FROM nested_category ORDER BY category_id;
这里将 left,right 修改为 lft,rgt因为这两个词在MYSQL中属于关键字 下面我们将插入的数据标识在图上: 点击查看原图
同样,我们将数据标识在原来的结构上:
点击查看原图
怎么样,是不是很明确了
下面使我自己标定一种形式,方便理解
[1
[2
[3 4]
[5 6]
[7 8]
9]
[10
[11
[12 13]
14]
[15 16]
[17 18]
19]
20]
遍历整个树,查询子集 条件:左边 > 父级L, 右边 < 父级R
SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;
+----------------------+
| name |
+----------------------+
| ELECTRONICS |
| TELEVISIONS |
| TUBE |
| LCD |
| PLASMA |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS |
| FLASH |
| CD PLAYERS |
| 2 WAY RADIOS |
+----------------------+
- 查询所有无分支的节点 条件:右边 = 左边L + 1
SELECT name
FROM nested_category
WHERE rgt = lft + 1;
- 查询某个字节点到根节点的路径
SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;
SELECT node.name, (COUNT(parent.name) - 1) AS depth
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;
- 查询子节点的深度
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;
- 插入新节点
算法详解:
1.所有分类 左边和右边的值 > 插入节点的左边节点记录的右值 的全部 + 2
2.插入节点 左值 = 插入位置左边节点记录的右值 + 1, 右值 = 插入位置左边节点记录的右值 + 2
例子:
在 R = 9(L8, R9)与 L = 10(L10,R11) 节点之间插入一个新节点
那么所有 左值 和 右值 > 9 的节点的左值和右值需要 + 2
例如新节点右边的节点(L10,R11)左值右值都需要 + 2 那么插入后的新值为 L12 R13
新节点的左值为 9 + 1 = 10 右值为 9 + 2 = 11
SQL语句实现
LOCK TABLE nested_category WRITE;
SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;
INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);
UNLOCK TABLES;
- 删除新节点
删除节点的算法与添加一个节点的算法相反
删除一个没有子节点的节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';
DELETE FROM nested_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;
UNLOCK TABLES;
删除一个分支节点和它所有的子节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';
DELETE FROM nested_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;
UNLOCK TABLES;
删除一个节点后移动其字节点到
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';
DELETE FROM nested_category WHERE lft = @myLeft;
UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight;
UNLOCK TABLES;
总结:
预排序遍历树算法的核心就是牺牲了写的性能来换取读取的性能
在你的开发的应用遇到此类问题的时(读压力 > 写压力),尝试下使用预排序遍历树算法来提高你的程序的性能吧。
http://be-evil.org/post-168.html
本文是我学习MySQL官方教程Managing Hierarchical Data in MySQL的笔记
多层数据结构估计所有的web开发者估计都不会陌生,各种软件的分类都是基于多层结构来设计的。
下面是一个典型的多层数据结构示意图:
点击查看原图
相关创建数据语句:
CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);
INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);
SELECT * FROM category ORDER BY category_id;
在这种数据结构中,各层之间通过字段 parent 来形成邻接表,我们查询某些层级的关系的时候一般都是通过递归的方式,遍历某个层级关系的SQL的查询次数会顺着层级的增加,想想在层级有20的时候,根据某个底层节点取它到顶层节点的查询次数吧。
为了解决这个问题,人们想出了嵌套集模型(The Nested Set Model),请看下图:
点击查看原图
上图依然是表现的与图一相同的层级关系,但是却更换了一种表现形式 下面是新的关系表和数据(关系和数据与之前相同,但是表结构不一样):
CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
INSERT INTO nested_category
VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),
(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);
SELECT * FROM nested_category ORDER BY category_id;
这里将 left,right 修改为 lft,rgt因为这两个词在MYSQL中属于关键字 下面我们将插入的数据标识在图上: 点击查看原图
同样,我们将数据标识在原来的结构上:
点击查看原图
怎么样,是不是很明确了
下面使我自己标定一种形式,方便理解
[1
[2
[3 4]
[5 6]
[7 8]
9]
[10
[11
[12 13]
14]
[15 16]
[17 18]
19]
20]
遍历整个树,查询子集 条件:左边 > 父级L, 右边 < 父级R
SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;
+----------------------+
| name |
+----------------------+
| ELECTRONICS |
| TELEVISIONS |
| TUBE |
| LCD |
| PLASMA |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS |
| FLASH |
| CD PLAYERS |
| 2 WAY RADIOS |
+----------------------+
- 查询所有无分支的节点 条件:右边 = 左边L + 1
SELECT name
FROM nested_category
WHERE rgt = lft + 1;
- 查询某个字节点到根节点的路径
SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;
SELECT node.name, (COUNT(parent.name) - 1) AS depth
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;
- 查询子节点的深度
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;
- 插入新节点
算法详解:
1.所有分类 左边和右边的值 > 插入节点的左边节点记录的右值 的全部 + 2
2.插入节点 左值 = 插入位置左边节点记录的右值 + 1, 右值 = 插入位置左边节点记录的右值 + 2
例子:
在 R = 9(L8, R9)与 L = 10(L10,R11) 节点之间插入一个新节点
那么所有 左值 和 右值 > 9 的节点的左值和右值需要 + 2
例如新节点右边的节点(L10,R11)左值右值都需要 + 2 那么插入后的新值为 L12 R13
新节点的左值为 9 + 1 = 10 右值为 9 + 2 = 11
SQL语句实现
LOCK TABLE nested_category WRITE;
SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;
INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);
UNLOCK TABLES;
- 删除新节点
删除节点的算法与添加一个节点的算法相反
删除一个没有子节点的节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';
DELETE FROM nested_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;
UNLOCK TABLES;
删除一个分支节点和它所有的子节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';
DELETE FROM nested_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;
UNLOCK TABLES;
删除一个节点后移动其字节点到
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';
DELETE FROM nested_category WHERE lft = @myLeft;
UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight;
UNLOCK TABLES;
总结:
预排序遍历树算法的核心就是牺牲了写的性能来换取读取的性能
在你的开发的应用遇到此类问题的时(读压力 > 写压力),尝试下使用预排序遍历树算法来提高你的程序的性能吧。
发表评论
-
centos安装denyhost
2020-11-21 14:29 475安装denyhost 下面这个不知是否是官网。 https: ... -
rabbitmq的终端管理命令rabbitmqadmin
2020-11-13 17:03 891启动rabbitmq systemctl start rab ... -
centos 安装 RabbitMQ
2020-11-11 14:40 487安装 erlang语言环境。 千万不要直接 yum inst ... -
centos的证书配置
2020-06-09 20:32 988用百度云,发现对其他https 的 网站访问时有时无。 排查 ... -
git常用命令
2020-02-03 15:27 392git常用命令 删除本地分支。 git branch -d ... -
shadowsocks 故障解决
2020-02-01 12:06 4837今天突然发现shadowsocks无法使用。 解决方案:修改服 ... -
putty设置正向代理
2019-08-21 21:17 660利用putty设置正向代理。 假如你的电脑不能访问服务器B, ... -
centos7下vsftp的虚拟用户安装和配置
2019-07-12 20:05 953安装ftp在centos7下, 此类文章很多,这里也只是总结一 ... -
使用systemd为php程序建立守护进程
2019-01-14 16:47 2193前提,是centos7,或ubuntu对应版本。 以前需要 ... -
centos7.4安装php7.2套件
2018-08-24 12:07 3064# 操作系统 centos7.4 # 所谓套件,就是nginx ... -
centos7.4安装php5.6套件
2018-08-15 18:09 2106本文不包括清理系统原有环境里的nginx,php,mysql ... -
mysql数据库导入导出防止乱码,加用户
2017-06-19 10:42 627数据库原本在linux上,项目的编码是gbk。 现在想导入到w ... -
centos7 时间同步
2017-06-15 15:41 909# 安装 yum install chrony # 启 ... -
mysql设用户设密码
2013-12-11 16:45 779都是抄的。 添加最高权限用户 假设root的密码是passw ... -
获得linux文件夹下的所有文件(强化版)
2013-04-26 10:07 956class Sys { /** ... -
使nginx拒绝特定url(实际是目录)下所有文件的请求
2013-03-26 14:56 1452if ($request_uri ~* (.*)(\ ... -
使用php充当shell脚本(转载)
2013-02-02 09:17 1165任务:过滤出2010-08-18的apache访问日志,并放到 ... -
linux常用故障排查
2013-01-28 08:57 908linux 下查找大于100M的文件 find . - ... -
网卡流量监控(转载文章)
2012-12-11 10:45 949http://www.vpseek.com/newbies-g ... -
postgresql安装
2012-12-06 20:12 912# TYPE DATABASE USER ...
相关推荐
改进的前序遍历算法可以借助递归实现,遍历过程中不仅访问当前节点,还要将所有子节点(包括孙节点、曾孙节点等)都纳入遍历范围。以下是一个C#的递归实现示例: ```csharp public static void ...
本文介绍了一种基于前序遍历树的非递归方法,这种方法在大数据量场景下更高效。 首先,我们需要创建一个存储分类的数据表,这里是一个名为`category`的示例表结构: ```sql CREATE TABLE IF NOT EXISTS `category`...
通常,使用预排序遍历树(Preorder Traversal)或后序遍历(Postorder Traversal)可以有效减少递归深度,降低内存使用。另外,通过缓存结果,避免重复计算,也能显著提高性能。 5. **DataTree.php**: 这个文件很...
本文将详细讲解如何在Laravel 5中实现无限极分类递归,以及提供的压缩包文件内容及其用途。 首先,无限级分类通常基于自关联的数据库表结构,这意味着每个分类都可以有父分类,也可以有子分类,形成一个树状结构。...
4. **预排序遍历树(Pre-Order Tree Traversal,PTT)**:在数据库查询时,通过一次遍历获取所有分类,并在代码中构造树形结构。 **在ThinkPHP 3.2.3中的实现:** 这个示例可能包括以下部分: 1. 数据库表设计:...
在实现无限极分类下拉框时,关键在于递归遍历数据源,将具有层级关系的数据逐层展开并添加到下拉框中。具体步骤如下: 1. **数据源准备**:首先,需要一个数据源,这个数据源应该包含每个分类项的唯一标识符(ID)...
对于无限极树,遍历可能需要递归实现。例如,前序遍历的实现: ```java public void preOrderTraversal(TreeNode node) { if (node != null) { System.out.println(node.getName()); for (TreeNode child : node...
在PHP编程中,无限极分类递归排序是一个常见的需求,特别是在处理树形结构的数据时,如网站导航菜单、组织架构或者商品分类等。无限极分类意味着一个分类可以有任意多的子分类,而递归排序则能确保这些子分类按照...
在ASP(Active Server Pages)开发中,无限极分类是一种常见的需求,特别是在构建网站内容管理系统时。这个"asp 无限极分类完整实例"提供了一个完整的解决方案,帮助开发者处理具有层级关系的数据,例如:导航菜单、...
通过递归遍历菜单树,找到目标节点后进行更新即可。如果目标节点的子节点也需要修改,同样可以通过递归来实现。 **6. 移动菜单项** 移动菜单项涉及到更改节点的父节点。这需要递归地找到目标节点,然后将其从当前父...
无限极分类的基本思想是利用递归或者自连接的方式来实现。在这个实例中,我们可以假设有一个数据库表,其中包含`id`(主键)、`name`(分类名称)和`parent_id`(父分类ID)字段。通过`parent_id`,我们可以确定每个...
php无限极分类函数包,下载即可用,绝对好用,里面有多种无限极分类函数,可以参考,我都试过了
首先,我们来详细讲解**递归无限极分类**。这种方法基于函数的自我调用来实现。假设我们有一个`categories`表,其中包含`id`(主键)、`name`(类别名)和`parent_id`(父类ID)字段。我们可以通过以下步骤实现递归...
3. **预排序遍历树(Preorder Traversal)**:在遍历数据库获取分类时,通常采用预排序遍历,即先访问根节点,再访问左子树,最后访问右子树。这种方式便于构建无限级分类的层级关系。 4. **存储方式**:无限级分类...
在IT行业中,尤其是在数据库设计和数据展示领域,无限极分类是一种常见的需求。它允许我们创建一个没有固定深度的层级结构,比如网站导航菜单、组织架构、产品类别等。C#作为.NET框架的主要编程语言,提供了多种实现...
本项目"jQuery递归无限极树状菜单"就是这样一个解决方案,它利用jQuery的灵活性和CSS的样式控制,实现了可扩展的无限级树形菜单。 首先,我们需要理解jQuery的核心概念。jQuery是一个JavaScript库,它简化了HTML...
php无限极分类,手动排序,排序,无限极分类排序,自己填写排序
在IT领域,递归是一种强大的编程技术,常用于解决复杂问题,如数据结构的遍历、树形结构的处理等。在这个场景中,我们关注的是如何利用递归来实现无限极评论的功能。无限极评论通常应用于社交网络、论坛或博客系统,...