mysql怎么建立层次结构_使用 MySQL 管理层次结构的数据

3邻接表(Adjacency List)模型

由于使用纯 SQL 处理邻接表模型存在种种不便,因此 Mike Hillyer 郑重的介绍了嵌套集(Nested Set)模型。当使用这种模型时,我们把层次结构的节点和路径从脑海中抹去,把它们想象为一个个容器:

127641561_14_20180319110429613

可以看到层次关系没有改变,大的容器包含子容器。我们使用容器的左值和右值来建立数据表:

127641561_15_20180319110429785

需要注意left和right是 MySQL 的保留字,因此使用标识分隔符来标记它们。

插入数据:

127641561_16_20180319110429894

查看内容:

127641561_17_20180319110429956

可以看到:

127641561_18_2018031911043019

我们是如何确定左编号和右编号的呢,通过下图我们可以直观的发现只要会数数即可完成:

127641561_19_20180319110430113

回到树形模型该怎么处理,通过下图,对数据结构稍有概念的人都会知道,稍加改动的先序遍历算法即可完成这项编号的工作:

127641561_20_20180319110430253

127641561_7_20180319110428988   获取整棵树

一个节点的左编号总是介于其父节点的左右编号之间,利用这个特性使用 self-join 链接到父节点,可以获取整棵树:

127641561_21_20180319110430394

结果如下:

127641561_22_20180319110430488

但是这样我们丢失了层次的信息。怎么办呢?使用COUNT()函数和GROUP BY子句,可以实现这个目的:

127641561_23_20180319110430628

需要注意 MySQL 5.7.5 开始默认启用了only_full_group_by模式,让GROUP BY的行为与 SQL92 标准一致,因此直接使用ORDER BY node.`left`会产生错误:

127641561_24_20180319110430722

使用ANY_VALUE() 是避免这个问题的一种的途径。

结果如下:

127641561_25_20180319110430894

稍作调整就可以直接显示层次:

127641561_26_201803191104303

结果相当漂亮:

127641561_27_20180319110431128

当然客户端代码可能会更倾向于使用depth值,对返回的结果集进行循环,Web 开发人员可以根据其增大或减小使用

/或
  • /
等。

127641561_7_20180319110428988   获取节点在子树中的深度

要获取节点在子树中的深度,我们需要第三个 self-join 以及子查询来将结果限制在特定的子树中以及进行必要的计算:

127641561_28_20180319110431253

结果是:

127641561_29_20180319110431410

127641561_7_20180319110428988   寻找一个节点的直接子节点

使用邻接表模型时这相当简单。使用嵌套集时,我们可以在上面获取子树各节点深度的基础上增加一个HAVING子句来实现:

127641561_30_20180319110431488

结果:

127641561_31_20180319110431660

127641561_7_20180319110428988   获取所有叶节点

观察带编号的嵌套模型,叶节点的判断相当简单,右编号恰好比左编号多 1 的节点就是叶节点:

127641561_32_20180319110431753

结果如下:

127641561_33_20180319110431816

127641561_7_20180319110428988  获取单个节点的完整路径

仍然是使用 self-join 技巧,不过现在无需顾虑节点的深度了:

127641561_34_20180319110431925

结果如下:

127641561_35_2018031911043235

127641561_7_20180319110428988   聚集操作

我们添加一张releases表,来展示一下在嵌套集模型下的聚集(aggregate)操作:

127641561_36_20180319110432160

加入一些数据,假设这些数据是指某个软件支持的发行版:

127641561_37_20180319110432269

那么,下面的查询可以知道每个节点下涉及的发布版数量,如果这是一个软件支持的发布版清单,或许测试人员想要知道他们得准备多少种虚拟机吧:

127641561_38_20180319110432363

结果如下:

127641561_39_20180319110432441

如果层次结构是一个分类目录,这个技巧可以用于查询各个类别下有多少关联的商品。

127641561_7_20180319110428988   添加节点

再次回顾这张图:

127641561_19_20180319110430113

如果我们要在Gentoo之后增加一个Slackware,这个新节点的左右编号分别是 10 和 11,而原来从 10 开始的所有编号都需要加 2。我们可以:

127641561_40_20180319110432597

使用之前掌握的技巧看一下现在的情况:

127641561_41_20180319110432800

结果为:

127641561_42_20180319110432941

如果新增的节点的父节点原来是叶节点,我们需要稍微调整一下之前的代码。例如,我们要新增Slax作为Slackware的子节点:

127641561_43_2018031911043366

现在,数据形如:

127641561_44_20180319110433206

127641561_7_20180319110428988   删除节点

删除节点的操作与添加操作相对,当要删除一个叶节点时,移除该节点并将所有比该节点右编码大的编码减 2。这个思路可以扩展到删除一个节点及其所有子节点的情况,删除左编码介于节点左右编号之间的所有节点,将右侧的节点编号全部左移该节点原编号宽度即可:

127641561_45_20180319110433331

可以看到Slackware子树被删除了:

127641561_46_20180319110433425

稍加调整,如果对介于要删除节点左右编号直接的节点对应编号左移 1,右侧节点对应编号左移 2,则可以实现删除一个节点,其子节点提升一层的效果,例如我们尝试删除 RHEL但保留它的子节点:

127641561_47_20180319110433550

结果为:

127641561_48_20180319110433675

127641561_49_20180319110433785

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值