假设您有一个存储有序树层次结构的平面表: sql
Id Name ParentId Order 1 'Node 1' 0 10 2 'Node 1.1' 1 10 3 'Node 2' 0 20 4 'Node 1.1.1' 2 10 5 'Node 2.1' 3 10 6 'Node 1.2' 1 20
这是一个图,咱们有[id] Name
。 根节点0是虚构的。 数据库
[0] ROOT / \ [1] Node 1 [3] Node 2 / \ \ [2] Node 1.1 [6] Node 1.2 [5] Node 2.1 / [4] Node 1.1.1
您将使用哪一种简约方法将其做为正确排序,正确缩进的树输出到HTML(就此而言,仍是文本)? 数组
进一步假设您只有基本的数据结构(数组和哈希图),没有带有父/子引用的奇特对象,没有ORM,没有框架,只有两只手。 该表表示为结果集,能够随机访问。 数据结构
可使用伪代码或简单的英语,这纯粹是一个概念性问题。 框架
额外的问题:在RDBMS中是否存在从根本上更好的方法来存储这样的树结构? nosql
编辑和添加 工具
要回答一个评论者( Mark Bessey )的问题:根节点不是必需的,由于它永远不会显示。 ParentId = 0是表示“这些是顶级”的约定。 Order列定义如何对具备相同父代的节点进行排序。 spa
我所说的“结果集”能够图片为一个哈希表数组(保留在该术语中)。 对于个人示例,本应已经存在。 一些答案须要付出额外的努力,而后再进行构建,但这不要紧。 code
这棵树能够任意深。 每一个节点能够有N个子节点。 不过,我并无真正想到“成千上万的条目”树。 对象
不要将我对节点命名(“节点1.1.1”)的选择误认为是要依赖的东西。 这些节点也能够称为“ Frank”或“ Bob”,没有暗示命名结构,这仅仅是为了使其可读。
我已经发布了本身的解决方案,所以大家能够将它分解成碎片。
考虑将noeo工具(例如neo4j)用于层次结构。 例如,诸如linkedin之类的联网应用程序使用couchbase(另外一个nosql解决方案)
可是,仅将nosql用于数据集市级别的查询,而不用于存储/维护事务
假设您知道根元素为零,这是要输出到文本的伪代码:
function PrintLevel (int curr, int level) //print the indents for (i=1; i<=level; i++) print a tab print curr \n; for each child in the table with a parent of curr PrintLevel (child, level+1) for each elementID where the parentid is zero PrintLevel(elementID, 0)
若是能够建立嵌套的哈希图或数组,那么我能够简单地从表头开始将表格添加到嵌套数组中。 我必须跟踪每行到根节点,以便知道要插入嵌套数组中的哪一个级别。 我可使用记忆,这样我就不须要一遍又一遍地查找同一个父母。
编辑:我将首先将整个表读入数组,所以不会重复查询数据库。 固然,若是您的桌子很大,这将不切实际。
构建完结构后,我必须先遍历深度并打印出HTML。
没有更好的基本方法来使用一个表存储此信息(尽管我可能错了;),而且但愿看到一种更好的解决方案)。 可是,若是您建立一个方案来使用动态建立的db表,那么您将在牺牲简单性和SQL地狱的风险的同时开辟了一个全新的世界;)。
您可使用哈希图模拟任何其余数据结构,所以这不是一个可怕的限制。 从顶部到底部扫描,您将为数据库的每一行建立一个哈希图,并为每一列建立一个条目。 将每一个哈希表添加到键入ID的“主”哈希表。 若是任何节点具备还没有出现的“父”节点,请在主哈希图中为其建立一个占位符条目,并在看到实际节点时将其填充。
要打印出来,请对数据进行简单的深度优先传递,并始终跟踪缩进级别。 经过为每一行保留一个“子项”条目,并在扫描数据时填充它,可使此过程变得更加容易。
至因而否有一种“更好”的方式将树存储在数据库中,这取决于您如何使用数据。 我已经看到了具备已知最大深度的系统,该系统为层次结构中的每一个级别使用了不一样的表。 若是树中的级别毕竟不彻底相等(顶层类别与叶子不一样),这颇有道理。
好的选择,我会使用对象。 我会为每一个记录建立一个对象,其中每一个对象都有一个children
对象集合,并将它们所有存储在ID为键的assoc数组(/ hashtable)中。 并快速浏览该收藏集,将子级添加到相关的子级字段中。 简单。
可是因为限制某些良好的OOP的使用对您很无聊,所以我可能会根据如下条件进行迭代:
function PrintLine(int pID, int level) foreach record where ParentID == pID print level*tabs + record-data PrintLine(record.ID, level + 1) PrintLine(0, 0)
编辑:这相似于其余几个条目,但我认为它稍微干净一些。 我要添加的一件事:这是很是消耗SQL的。 真讨厌 若是能够选择,请执行OOP路线。