树形表的平行查询设计

本文由网友长空X投稿,欢迎转载、分享

原文作者:长空X(CSDN同名“长空X“,CkTools的作者,github: https://github.com/hjkl950217)

原文链接:https://www.cnblogs.com/gtxck/articles/16293295.html

起因

今天在和懒得勤快[1]聊天时谈到了树形表的处理时,发现目前我俩知道的查树形表都得递归查询,这种方式查询效率是非常底下且不好维护的,那么有没有一种又简单能平行查询的方式呢?后面我俩还真讨论了一种,他快速的修改到他的网站中了。

懒得勤快官网

声明

文章中的几个方案是我们的讨论结果和一部分网络资料总结。设计方式千万种,文章中介绍的设计方式是针对大部分需要树形表的情况而不代表最优解!最优解已经是集合设计方式人员水平业务情况等因素综合之后的方案,这篇分享只是加速找到你的最优解

什么是树形表?

关系型数据库表中,存放树形结构的表。例如某个字段需要选择分类,有一级、二级、...N级,可以这样设计:

IDPID名字或内容
1
评论1
21评论2
31评论3
43评论4
9242a78985677adb71935f4bf69655d7.png

这样的数据可以组合成我们大学数据结构中的,用来表达层级关系。这里的Id一般情况下用数字最好,但也有不是数字的情况,这点对选择方案可能有影响,后面会提到这一点。这种数据结构的实体定义一般如下:

class CommentEntity
{public int ID {get;set;}public int PID {get;set;}//.. 若干数据字段public CommentEntity ParentNode {get;set;}public List<CommentEntity> ChildNode {get;set;}
}

实体定义ParentNode指向父节点,ChildNode指向若干子节点。如果你有数据结构中的链表知识,能看出这2个字段起指针域的作用。

数据在数据库中按存储,如果我们将数据获取出来后组装好ParentNodeChildNode中的指向,然后就能按你的实际业务情况使用了。

有什么用?

有所属关系的都可以用这种方式存,例如: 权限关系、分类、类型、级别划分、行政区划、评论等等等...

但他麻烦之处在于查询不方便。比如想要查询一级分类下面的所有数据,按传统方式需要先查到id=1的一级分类,再查询PID=1的数据,再查询PID=刚才查询的数据ID 这样递归查询多次直到结束

目标

我们以评论为例

4b7daf6d16407ac9455a227da4d3706a.png

需要满足:

  1. 进页面时分页查询出主评论,然后按层次关系显示回评

  2. 可以根据某一个评论查询下属所有评论

  3. 平行查询而不是递归查询

  4. 每个评论数据可以是主评判,也可以是子评论

方案1: 使用tag标记树

这个方案是添加一个字段tag来标记整颗树,结构如下:

IDPIDTag内容
1
文章Id1评论1
21文章Id1评论2
31文章Id1评论3
43文章Id1评论4

Tag用于数据库查询,ID和PID用于内存中组装数据,同时对Tag这一列建立非聚集索引。

查询方式

这里新增的字段在每课树中都是一样的,最多查询2次数据库即可,然后自己在内存中用Pid重新排列引用关系,修剪掉不需要的数据。

第一次查询:用评论id查询出文章id(有文章Id时直接第二步)

第二次查询:用文章id查询出所有数据

分页查询:查询后在内存中修剪掉不需要的数据

这种设计基于这些考虑:

  1. Id是数字的情况下,连续的数据大概率在磁盘上是连续存储,这能提高磁盘IO的效率。如果Id不是数字,用文章Id创建非聚集索引后也能快速查询。

  2. 在内存中组装引用关系是非常快的,而且不需要递归就能搞定.(遍历时用PID去查找,找到后直接向ChildNode添加,同时向ParentNode赋值)

  3. 设计逻辑简单,实习生水平以上的人就能轻松维护这种代码

缺点:如果一颗评论树有1000层,那无疑会获取巨量的无用数据

改进:使用level标记级别

增加级别字段:

IDPIDtaglevel内容
1
文章Id11评论1
21文章Id12评论2
31文章Id12评论3
43文章Id13评论4

查询时附加上level,能减少一部分无用数据的传输,最后复用上面的组装代码。

方案2: 使用path标记依赖路径

借用网上的一张图直接说明思路(未找到出处,侵权删除):

deb934c31bf052b1d9a4169b3e529923.png

结合上面说的改造一下:

IDPIDTagPath内容
1
文章Id1
评论1
21文章Id11评论2
31文章Id11评论3
43文章Id11,2评论4

在写入子节点时需要知道父节点的path,但一般来说这点是能满足的。Tag和Path用于数据库查询,ID和PID用于内存中组装数据。

查询方式

查询全部:仍文章id查询所有数据,然后在内存中用Pid组装

查询id为2及下面的数据:

第一次查询:查询id=2的path

第二次查询:查询  id=2 or startwith $"{path},2"

分页查询:

先用文章id按时间排序后查询前X个,然后进行第2次查询获取楼中楼的数据,第2次查询时可以拼多个 startwith

同时也建议按需冗余level字段以减少查询,path中虽然隐含了级别数据,但在查询时并不友好。

这种设计基于这些考虑:

  1. 同方案1差不多,并且理解成本更低

缺点:不算特别的缺点,在查询子节点数据用path过滤时,是利用不上索引的。

方案3: 不设计楼中楼

借鉴知乎的设计,一看就懂系列:

93c5dfbb1d9687ec840fa9447c2ba0d6.png

知乎的结构中只有评论和回评,回评也只需要保存上一次评论的id即可。这种方式不光设计简单,阅读体验也极好(楼中楼深了并非不好看)

IDPIDGroupIDTag内容
1
1文章Id1评论1
211文章Id1评论2
311文章Id1评论3
431文章Id1评论4
5
2文章Id1评论5

查询方式

查询全部:仍文章id查询所有PID is null的数据,然后在内存中用PID组装

查询id为1及下面的数据:查询 GroupID = 1的数据。这种设计时不会单独查询回评的数据

优点:理解成本非常低,同时存储压力也小

方案4:使用递归

前面不是说不使用递归吗?为什么这里还要提呢?因为:

  1. 有些团队中有人会固执的认为数据库不应该返回额外数据,也不应加冗余节点

  2. mysql 8.0 中增加了RECURSIVE来在数据库层面实现递归

  3. 其它无奈

所以如果前面3种方案都不适合你的情况,可能你还得回到递归这条路线上面,具体的这里就不提了,网上有许多这类文章。

总结

方案123都是通过冗余字段来降低查询成本和理解成本,并且利用不同存储的特性(数据库不适合运算、内存适合快速读写)来实现目标

方案3也是,同时也通过分析优化业务实现技术成本客户体验的共赢。

方案4为兜底方案。

我个人比较推崇level+path的组合,这个组合不光能处理评论,也能很好的处理其它的树形结构,毕竟开发人员不能总是有机会影响业务需求不是?

如果你有更好的方案,欢迎留言讨论哦~

参考资料

[1]

懒得勤快: https://masuit.org/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 16.04安装SQLite Browser操作SQLite数据库

安装&#xff1a; sudo apt-get install sqlitebrowser 启动&#xff1a;

(13)python 字典 2 分钟速解

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

计算机绘画作品 星空,关于近期绘画作品《星空系列》的自述:

《星空系列》是一个很偶然的念想下产生的&#xff0c;但又绝非偶然。最开初的点是高一刚开始学画的时候&#xff0c;高中的老师给我们免费提供油画材料给我们练习&#xff0c;那时候我并没有“安分”地练习高考绘画&#xff0c;总想做一些关于自己想法的作品&#xff0c;那时候…

BootStrap导航栏的使用

默认的导航栏 创建一个默认的导航栏的步骤如下&#xff1a; 向 <nav> 标签添加 class .navbar、.navbar-default。向上面的元素添加 role"navigation"&#xff0c;有助于增加可访问性。向 <div> 元素添加一个标题 class .navbar-header&#xff0c;内部包…

安装SQL Server 2005时,出现“SQL Server 服务无法启动。……”提示的解决方法

安装SQL Server 2005时&#xff0c;出现“SQL Server 服务无法启动。……”提示的解决方法上午在自己XP SP3电脑上安装SQL Server 2005时出现如下提示&#xff1a; 根据这一情况&#xff0c;我把自己的解题步骤写下来和大家分享&#xff0c;虽然简单了些&#xff0c;可是安装的…

.NET MAUI 正式发布,再见了 Xamarin.Forms

David Ortinau 在dotnet 团队博客上发表了一篇文章《Introducing .NET MAUI – One Codebase, Many Platforms》&#xff0c;在这篇文章里宣布了MAUI的正式发布。https://github.com/dotnet/maui/releases/tag/6.0.312 https://devblogs.microsoft.com/dotnet/introducing-dotn…

利用SQL注入获取服务器最高权限

单位有台数据库服务器&#xff08;windows 2000 操作系统&#xff0c;sql server 2000&#xff09;前段时间莫名其妙的被***了跑到机房&#xff0c;通过PE进去一看&#xff0c;发现多了一个账户&#xff08;SQLDEBUG)。并且administrator账户被禁用了看看数据没少&#xff0c;也…

3分钟搞定 C++ if else 语句 05

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。系列教程将会…

Android之封装倒计时页面

1 、需求 多个页面需要用到显示定时器页面,页面里面时间会一秒一秒减少,页面布局如下,开了定时器,如果其它页面也打开会使用之前的页面里面显示的时间,也就是说在有效范围内,时间不刷新。 2 、关键点 使用Android自带的倒计时类CountDownTimer CountDownTimer mCount…

1小时学会不打代码制作一个网页精美简历(1)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。系列教程将会在…

分部方法 partial

当有如下这样类似的情况出现的时候&#xff0c;可以有更好的优化方式来处理&#xff0c;那就是分部方法 1 class PartOld2 {3 string name;4 5 public virtual void OnChangeName(string str)6 {7 }8 9 public string Name 10…

一文讲透为Power Automate for Desktop (PAD) 实现自定义模块

今天写了一篇长文&#xff0c;《一文讲透为Power Automate for Desktop (PAD) 实现自定义模块 - 附完整代码》&#xff0c;有兴趣的同学点击 “阅读原文” 参考 &#xff0c;文章地址是 https://www.cnblogs.com/chenxizhang/p/16287195.html 微软的PAD是RPA的一种&#xff0…

软件自动化测试的主要目的,功能测试的目的是什么

从功能测试到自动化测试&#xff1f;从常见的自动化测试过程开始&#xff01;在项目的启动阶段功能测试 目的&#xff0c;我们必须阐明为什么要进行自动化&#xff0c;并选择合适的项目进行自动化测试. 1.为什么要进行自动化&#xff1f;在开始自动化测试之前&#xff0c;我们需…

基于Google Earth Pro做数字化,并在ArcGIS中转为Shapefile图层

在精度满足要求的前提下,可以基于Google Earth Pro做数字化,并在ArcGIS中转为Shapefile图层,然后经行投影转换,得到最终的shp数据。主要步骤包括:新建多边形、导出多边形、格式转换,投影转换等。 一. 新建多边形 命名为皇城水库范围,设置线条和面的样式/颜色,并在影像上…

1小时,不会代码的我如何完成 网易云音乐 大作业网页制作?(IVX 第2篇)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。系列教程将会在…

C# 第一个LINQ 查询

我们将先介绍一个示例&#xff0c;这个示例使用 LINQ 创建了一个查询&#xff0c;以便在一个简单的内存对象数组中查找一些数据&#xff0c;并输出到控制台上。试一试&#xff1a;第一个 LINQ 程序&#xff1a;按照下面的步骤在 Visual Studio 中创建示例&#xff1a; using Sy…

1小时赚300块,不打代码帮人做个吃鸡网页 [IVX实战第3篇]

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。 实战目录 1…

ArcGIS10.6中,在3D分析工具中创建视线之后,怎么将其删除?

如下图所示&#xff0c; 为3D分析工具条&#xff1a; 在3D分析中创建的线&#xff0c;例如做剖面图是插入的线&#xff0c;该怎样删除呢&#xff1f; 如下图所示。 删除方法是&#xff0c;先使用如下选择工具&#xff0c;然后双击线条&#xff0c;再按delete键。

RPC的原理总结

一.RPC的引入 早期单机时代&#xff0c;一台电脑上运行多个进程&#xff0c;大家各干各的&#xff0c;老死不相往来。假如A进程需要一个画图的功能&#xff0c;B进程也需要一个画图的功能&#xff0c;程序员就必须为两个进程都写一个画图的功能。这不是整人么&#xff1f;于是就…

怎样实现MathType中带箭头向量的输入

2019独角兽企业重金招聘Python工程师标准>>> 一个向量可以有多种记法&#xff0c;如记作粗体的字母&#xff08;a&#xff09;&#xff0c;或在字母顶上加一小箭头→&#xff0c;或在字母下加波浪线~。如果给定向量的起点&#xff08;A&#xff09;和终点&#xff0…