本文由网友
长空X投稿,欢迎转载、分享原文作者:天空X(CSSDN同名“天空X”,CkToolsの作者,github https//gitub.com/hjkl950217)
原文へのリンク:https//www.cnblogs.com/gtxck/articles/16293295.html
原因は、
今天在和懒得勤快聊天时谈到了树形表的处理时,发现目前我俩知道的查树形表都得递归查询,这种方式查询效率是非常底下且不好维护的,那么有没有一种又简单能平行查询的方式呢?后面我俩还真讨论了一种,他快速的修改到他的网站中了。
声明文は
文章中的几个方案是我们的讨论结果和一部分网络资料总结。设计方式千万种,文章中介绍的设计方式是针对大部分需要树形表的情况而不代表最优解!最优解已经是集合设计方式、人员水平、业务情况等因素综合之后的方案,这篇分享只是加速找到你的最优解。
ツリーテーブルとは?
关系型数据库表中,存放树形结构的表。例如某个字段需要选择分类,有一级、二级、...N 级,可以这样设计:
| ID | PID | 名前や内容。 |
|---|---|---|
| 1 | コメント1件 | |
| 2 | 1 | コメント2 |
| 3 | 1 | コメント3。 |
| 4 | 3 | コメント4。 |

这样的数据可以组合成我们大学数据结构中的树,用来表达层级关系。这里的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 个字段起指针域的作用。
数据在数据库中按行存储,如果我们将数据获取出来后组装好ParentNode和ChildNode中的指向,然后就能按你的实际业务情况使用了。
何の役に立つ?
有所属関系的都可以用这种方式存,例:権限関系,,区分,行政区画,等。.
但他麻烦之处在于查询不方便。比如想要查询一级分类下面的所有数据,按传统方式需要先查到id=1的一级分类,再查询PID=1的数据,再查询PID=刚才查询的数据ID 这样递归查询多次直到结束
ターゲットは
コメントを例にとる。

満たす必要がある:
- 进页面时
分页查询出主评论,然后按层次关系显示回评 - 1つのコメントに基づいて部下のすべてのコメントを照会できます
- 再帰クエリではなく平行クエリ
- 各レビューデータは、メインまたはサブレビューになります。
シナリオ1 tagツリーの使用
这个方案是添加一个字段tag来标记整颗树,结构如下:
| ID | PID | Tag | 内容は |
|---|---|---|---|
| 1 | ID1の記事 | コメント1件 | |
| 2 | 1 | ID1の記事 | コメント2 |
| 3 | 1 | ID1の記事 | コメント3。 |
| 4 | 3 | ID1の記事 | コメント4。 |
Tag用于数据库查询,ID和PID用于内存中组装数据,同时对Tag这一列建立非聚集索引。
** お問い合わせ **:
这里新增的字段在每课树中都是一样的,最多查询 2 次数据库即可,然后自己在内存中用Pid重新排列引用关系,修剪掉不需要的数据。
第一次クエリ:コメントidを用いて文章 idをクエリアウトする文章 Idがある場合は直接第二ステップ
2番目のクエリ:記事IDですべてのデータを検索する
ページングクエリ:クエリ後に不要なデータをメモリ内でトリミングする
** この設計はこれらの考慮事項に基づいています **
- Id 是数字的情况下,连续的数据
大概率在磁盘上是连续存储,这能提高磁盘 IO 的效率。如果 Id 不是数字,用文章Id创建非聚集索引后也能快速查询。 - 在内存中组装引用关系是非常快的,而且不需要递归就能搞定.(遍历时用 PID 去查找,找到后直接向
ChildNode添加,同时向ParentNode赋值) - 設計ロジックが簡単で,実習生レベル以上の人でも簡単にこのコードをメンテナンスできる
短所:コメントツリーが1,000層ある場合、間違いなく大量の無駄なデータを取得します。
改善:レベルタグ付きレベルを使用
レベルの増加フィールド
| ID | PID | tag | level | 内容は |
|---|---|---|---|---|
| 1 | ID1の記事 | 1 | コメント1件 | |
| 2 | 1 | ID1の記事 | 2 | コメント2 |
| 3 | 1 | ID1の記事 | 2 | コメント3。 |
| 4 | 3 | ID1の記事 | 3 | コメント4。 |
查询时附加上level,能减少一部分无用数据的传输,最后复用上面的组装代码。
シナリオ2 pathタグを使用してパスに依存する
借用网上的一张图直接说明思路(未找到出处,侵权删除):

上記の変更を考慮します。
| ID | PID | Tag | Path | 内容は |
|---|---|---|---|---|
| 1 | ID1の記事 | コメント1件 | ||
| 2 | 1 | ID1の記事 | 1 | コメント2 |
| 3 | 1 | ID1の記事 | 1 | コメント3。 |
| 4 | 3 | ID1の記事 | 1,2 | コメント4。 |
在写入子节点时需要知道父节点的 path,但一般来说这点是能满足的。Tag和Path用于数据库查询,ID和PID用于内存中组装数据。
** お問い合わせ **:
查询全部: 仍文章 id 查询所有数据,然后在内存中用Pid组装
クエリIDは2で、以下のデータがあります。
最初のクエリ:ID=2のパスをクエリする
2回目のクエリークエリー id=2 or with $" 2"
ページ検索:
先用文章 id 按时间排序后查询前 X 个,然后进行第 2 次查询获取楼中楼的数据,第 2 次查询时可以拼多个 startwith。
同时也建议按需冗余level字段以减少查询,path 中虽然隐含了级别数据,但在查询时并不友好。
** この設計はこれらの考慮事項に基づいています **
- プラン1とほぼ同じで、低コストを理解する
** 欠点 **:特に欠点ではなく、クエリ子ノードデータをパスフィルタリングする際にインデックスを利用できません。
案3楼中楼をしない
よく知られたデザインで、シリーズを見てください。

わかりやすい構造にはコメントとレビューのみがあり、レビューは最後のコメントのIDを保存するだけでよい。この方法はデザインが簡単であるだけでなく、読書体験も優れています(建物の中に深くても悪くない)。
| ID | PID | GroupID | Tag | 内容は |
|---|---|---|---|---|
| 1 | 1 | ID1の記事 | コメント1件 | |
| 2 | 1 | 1 | ID1の記事 | コメント2 |
| 3 | 1 | 1 | ID1の記事 | コメント3。 |
| 4 | 3 | 1 | ID1の記事 | コメント4。 |
| 5 | 2 | *** 記事ID1** | ** コメント5** |
** お問い合わせ **:
查询全部: 仍文章 id 查询所有PID is null的数据,然后在内存中用PID组装
查询 id 为 1 及下面的数据: 查询 GroupID = 1的数据。这种设计时不会单独查询回评的数据
利点:コストが非常に低く、保管圧力が小さいことを理解する
シナリオ4:再帰の利用
再帰を使わないと言ったでしょうか。なぜここで言及するのか?なぜなら、
- 一部のチームでは、データベースが余分なデータを返さず、冗長ノードを追加すべきではないと主張する人もいます。
- My SQL 8.0では、データベースレベルで再帰を実装するRECURSIVEを追加。
- その他の無効
ですから、前の3つのスキームのどれもあなたの状況に適していない場合は、再帰ルートに戻る必要があるかもしれませんが、ここでは言及しません。
まとめまとめまとめ
方案 123 都是通过冗余字段来降低查询成本和理解成本,并且利用不同存储的特性(数据库不适合运算、内存适合快速读写)来实现目标
方案 3 也是,同时也通过分析优化业务实现技术成本与客户体验的共赢。
プログラム4はプログラムのためです。
我个人比较推崇level+path的组合,这个组合不光能处理评论,也能很好的处理其他的树形结构,毕竟开发人员不能总是有机会影响业务需求不是?
より良い計画がある場合は、コメントを歓迎します。