添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本文介绍的方法基于多叉树的前序遍历序列,是所有数据库树结构存储方案中查询子树速度最快的方案。最早发表在这里" http://drinkjava2.iteye.com/blog/2353983 ",但那篇文章太啰嗦了,这是整理后的精简版,其实原理很简单,几句话就能说完。

目前常见的树形结构数据库存储方案有以下四种,但是都存在一定问题:
1)Adjacency List(邻接表):每个节点仅记录父节点主键。优点是简单,缺点是访问子树需要递归遍历,对数据库压力大(即使是支持递归SQL的数据库)。
2)Path Enumerations( 路径枚举):用一个字符串记录当前节点所在路径。优点是查询方便,缺点是占用空间大,查询需要使用like模糊方法,效率低,插入新记录时要手工更改此节点以下所有路径,维护不便。
3)Closure Table(闭包表):专门一张表维护Path,缺点是占用空间大,操作不直观。
4)Nested Sets (嵌套集):记录左值和右值,优点是查询子树无需递归,缺点是非常复杂、难操作。

本文介绍的基于树形结构的前序遍历序列方法,示意图如下:


如上图左边的树结构,映射在数据库里的结构见右图表格,注意整个表格是一个排好序的树结构的前序遍历序列,相同节点深度的排序以line为准。 表格的最后一行(或每个根节点)必须有一个END标记,level设为0,上表中的ID为主键,Level为树节点的深度。在以上基础上,只要一句SQL,就可以无递归查询出任意节点的所有子树节点, 比某些数据自带的基于递归原理的查询更高效。 假设节点的行号为X,level为Y,则查询整个子树的SQL为:
select * from tb where line>=X and line<(select min(line) from tb where line>X and level<=Y)
例如获取D节点及其所有子节点:
select * from tb where line>=7 and line< (select min(line) from tb where line>7 and level<=2)

它依据的原理很简单:按树结构的前序遍历序列存储的树结构,判断它的所有子结点,只要从这个节点开始往下划竖线,竖线右边的全是它的子节点,直到碰到竖线上或竖线左边出现非空值则终止,原理图如下:


这种方案的优点是查询高效,删除高效(只需要删除当前行即可,虽然会造成line字段的跳号,但是不影响使用),插入节点也很简单,只需要2行简单的SQL即可,例如要在line=9和line=10的两个节点之间插入一个新节点,则新节点行号应为10,原行号为10的将变为11, SQL操作为:
update tb2 set line=line+1 where line>=10; //把10以后的所有行号加1
insert into tb (line,id,level) values (10,'T',4); //执行插入新节点操作
虽然影响的行数非常多,但是理解和维护都非常简单。 如果担心插入操作对性能的影响,还可以将行号跳号设计(如按100000跳号),新的节点可以插入在两个节点的空号区中段(如果节点间存在空号区),这样可以很大程序上消除进行加1操作的概率。

这种方案的缺点是当需要移动节点时,必须维护整张表的前序遍历排序顺序,必要时要进行整张表或子树的重排序, 这是一个很耗时的工作(其它三种方案Path Enumerations/Closure Table/Nested Sets也有这个缺点),这是不可避免的,是以始终维护一个索引为代价换取查询性能的提高。因此它最适合只有增删查操作,但是很少有移动节点的场合。

总结:这种方案最适合的场合是树的深度很深(可以超过上百万),不经常移动节点、对查询速度要求极高的场合(因为无递归)。

本文实际上可以抽象成一种利用前序遍历给多叉树建查询索引的方案,即使在没有数据库存在的情况下,也是有可能在程序中应用到这种方案的,只要将SQL中的查询功能改成手工进行数组遍历即可。
另外,数据库开发者也可以考虑,对于邻接表模式(即只有id和pid两个关键字段)存储的树结构, 可以用这种方法创建一个内部索引,将level和line作为数据库表的隐藏字段,这样用户就可以不用手工维护level、line以及维护前序遍历排序这些与业务无关的操作,这才是最人性化的使用方式。

展开阅读全文 看了 wangEditor 的公告,如鲠在喉。去年七月,我在一篇《关于剔除 layedit 组件》的公告中,还推荐了几款 editor 组件用于替代,其中就包括了 wangEditor, 转眼之间,仿若时空交错,不免有些感慨。 在国内由个人发起的开源项目,似乎都很难跳出相同的宿命,不断有人走进这个赛道,但能抵达终点并完成突破的却屈指可数。Layui 曾经同样倒在了赛道,2021 年宣布关站之前,Layui 的百度指数还一度领先 Bootstrap, 如此一个拥有广泛用户群体的 UI 库,本该迎来新的突破,却在疾跑中戛然止步,至今令人迷惑。人们看到的,是一篇充满悲情色彩的公告,而公告的背后,是创作者在面对内外交织的困境中不得已做出的举措,当我们没有足够的力量冲破眼前的障碍,除了停下来避开它,还有别的更优选择么。你很难想象除此之外还有多少历史包袱… 譬如,也是由于种种原因,当初 Layui 在 Github 和 Gitee 待处理的 Issue 数量,不下于 2000,各种议题参次不齐,我差不多花了半年时间去逐一审阅,多少个日夜消耗,多少次自我情绪的对抗… 就不多提了。 尽管这两年来,Layui 的受众者已呈断崖式流失,但正因为小众,反而如释重负,甚至让我重新找回了一些开源的纯粹。 共勉 🙂 用过`php`, 有几个缺点用不下去: - `php`的动态语言特性造成经常`IDE`不能很好的跳转源码位置。 - 语言本身不支持调试,需要用插件,上`Apache`。 比如我想测试一个方法。 - 对某个文件进行单元测试麻烦。 - 变量命名很乱。 动态语言团队开发就是噩梦,比如突然从某个变量里访问某个`key`,都不知道这个`key`哪里来的,`IDE`支持不好的话得全部搜索一遍。 - 想要运行时进入第三方库并调试,难。 - 性能如果纯`php`并不高,有谁会经常写`C`扩展? - 如果想用线程,比如非实时定时任务,邮件发送,数据统计等。本身标准库不支持线程。 - 除了做网站方便点其他程序开发难做。比如想做个分析`Excel`的命令行小工具,不能打包为`exe`并发给别人用。 都是学语言,其他语言比如`java`,`go`还能做非网站程序,有的选学PHP干嘛?当然目前网站部署成本`php`最低,也支持热部署这个目前静态语言还超越不了。如果要学动态语言,用`javascript`开发网站后端也不错啊,还能把经验用在开发前端网页上。 我作为该项目的开发团队成员之一,在此说明: 1. 这个项目是完全由自己编写的,没有借助商业的帮助,没有他人冒充,也不存在父母帮我们编写后给我们挂名的问题。同时因为我们都是中学生,没有多少资金,也不可能进行"买广告"等不正当的商业行为; 2. 我们自己做的项目绝对没有博眼球,单纯就是想对Windows在网页上进行模仿和创新; 3. 我们的实力并不是那么差,我们开发团队成员曾获得C++二级全市第一、CSP-J国赛一等奖、蓝桥杯省赛一等奖等多个奖项,成员每人编程能力全国前4%-10%,并且我们人均精通前端和后端,就拿我来举例,我能熟练使用git命令行,从克隆仓库,提交、推送更改到拉取合并、暂存、设置远程、配置ssh密钥都烂熟于心,熟悉Linux系统,从查看当前目录下文件和文件夹到使用命令行手动安装驱动都熟悉,熟悉NodeJS,会下载、导入模块和程序编写与运行,精通HTML, CSS和JS,基本的东西都能灵活运用,并且熟悉Python, C, C++和汇编语言; 4. 我们项目链接在 https://github.com/tjy-gitnub/win12,现已经获得超1500颗Stars 本人原创作者,在此解释声明一二: 1. 我的父母还没有无耻到做这种无耻的事情,我也对我的能力有信心,目前也不需要这些偷鸡摸狗的见不得光的东西来造假,请各位不要以小人之心度君子之腹。 2. 我今年初三,项目是从初一开始做的,各位不相信的欢迎上github看提交记录,可以看一下初版和现在的区别,UI和js都有不小更改。 3. 团队组成:今年一位初三,一位初一,一位5+4制的初一。更新记录中明确记录了哪些功能是哪位所开发的。 4. 我在github上,bilibili上没有受到过任何一个人的质疑,我对中国的网络环境表示蔑视。 5. 本项目的初中只是为了兴趣,没有想到火了起来。 我不理解你们是如何通过代码读出作者年龄的?难道是语文考试要加入代码阅读赏析的题目了吗?真心觉得很厉害。 6. 不愿与某些人同流,也无众位深厚阅历经验,只望能得清白之名。感谢大家让我懂得了何乃人情世故,孰谓世态炎凉,世俗红尘。你们给我的人生上了重要的一课。人间哪有什么真善美啊,呵。社会的病胎罢了。