|
1
481
既然 MySQL 8.0 supports recursive queries all popular SQL databases support recursive queries 用标准语法。
我在演示文稿中测试了MySQL 8.0中的递归查询 Recursive Query Throwdown 以下是我2008年的原始答案: 在关系数据库中存储树结构数据有几种方法。您在示例中展示的内容使用两种方法:
另一种解决方案称为 嵌套集 ,并且它也可以存储在同一个表中。阅读“ Trees and Hierarchies in SQL for Smarties Joe Celko提供了关于这些设计的更多信息。 我通常喜欢一种叫做 闭合表 (也称为“邻接关系”),用于存储树结构数据。它需要另一个表,但查询树非常容易。 我在演讲中提到了闭包表 Models for Hierarchical Data with SQL and PHP 在我的书里 SQL Antipatterns: Avoiding the Pitfalls of Database Programming .
将所有路径存储在闭包表中,其中存在从一个节点到另一个节点的直接祖先。为每个节点包含一行以引用自身。例如,使用问题中显示的数据集:
现在,您可以从节点1开始创建一棵树,如下所示:
输出(在MySQL客户端中)如下所示:
换句话说,节点3和5被排除在外,因为它们是独立层次结构的一部分,而不是从节点1开始下降。
回复:e-satis对直系子女(或直系父母)的评论。您可以添加一个“
然后,您可以在搜索中添加一个术语,以查询给定节点的直接子节点。他们的后代
@ashraf的回复:“对整棵树[按名称]排序怎么样?”
下面是一个示例查询,用于返回作为节点1的后代的所有节点,并将它们连接到包含其他节点属性(例如
@Nate回复:
编辑建议上面最后一个查询中的ORDER BY应为
如果您希望排序以合理的方式匹配层次结构,这是可能的,但不仅仅是通过按路径长度排序。例如,请参见我的答案 MySQL Closure Table hierarchical database - How to pull information out in the correct order . |
|
|
2
61
如果使用嵌套集(有时称为修改的预排序树遍历),则可以通过单个查询按树顺序提取整个树结构或其中的任何子树,但插入的代价更高,因为您需要管理通过树结构描述顺序路径的列。 对于 django-mptt ,我使用了这样的结构: id parent_id tree_id level lft rght -- --------- ------- ----- --- ---- 1 null 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12
它描述了一棵看起来像这样的树(带有
1
+-- 2
| +-- 3
| +-- 4
|
+-- 5
+-- 6
+-- 7
或者,作为一个嵌套的集合图,它使
__________________________________________________________________________ | Root 1 | | ________________________________ ________________________________ | | | Child 1.1 | | Child 1.2 | | | | ___________ ___________ | | ___________ ___________ | | | | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | C 1.2.2 | | | 1 2 3___________4 5___________6 7 8 9___________10 11__________12 13 14 | |________________________________| |________________________________| | |__________________________________________________________________________|
如您所见,要获得给定节点的整个子树,只需选择所有具有
这个
有关MPTT的更多信息: |
|
|
3
24
这是一个相当古老的问题,但由于它有许多观点,我认为值得提出一个替代方案,而且在我看来是非常优雅的解决方案。 为了读取树结构,您可以使用 递归公共表表达式 (CTEs)。它提供了一种一次获取整个树结构的可能性,具有关于节点级别、其父节点以及父节点子节点中的顺序的信息。
|
|
|
4
18
从SQLServer2005开始,您可以使用递归公共表表达式(CTE)。
Name 'Node 1' ' Node 1.1' ' Node 1.1.1' ' Node 1.2' 'Node 2' ' Node 2.1' |
|
|
5
10
比尔的回答真是太好了,这个回答又增加了一些东西,这让我希望我的回答能得到支持。
无论如何,我想支持树结构和Order属性。我在每个名为
mysql> desc nodes ; +-------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | leftSibling | int(11) | NO | | 0 | | +-------------+--------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) mysql> desc adjacencies; +------------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------+------+-----+---------+----------------+ | relationId | int(11) | NO | PRI | NULL | auto_increment | | parent | int(11) | NO | | NULL | | | child | int(11) | NO | | NULL | | | pathLen | int(11) | NO | | NULL | | +------------+---------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) More detail and SQL code on my blog . 谢谢比尔,你的回答有助于入门! |
|
|
6
7
如果有选择,我会使用对象。我会为每个记录创建一个对象,其中每个对象都有一个
但是,由于限制使用一些好的OOP并不是一件有趣的事情,我可能会基于以下内容进行迭代:
肮脏的 如果你有选择的话,那就走面向对象的路线。 |
|
|
7
6
有一些非常好的解决方案利用了sql索引的内部btree表示。这是基于1998年前后所做的一些伟大的研究。 下面是一个示例表(在mysql中)。
树表示法所需的字段只有:
以下是示例24节点填充,按tw排序:
每个树结果都可以非递归地完成。 例如,获取tw='22'处节点的祖先列表 祖先
兄弟姐妹和孩子都很琐碎——只需使用tw对pa字段进行排序即可。
例如,以tw=17为根的节点集(分支)。
当读取的数量远大于插入或更新的数量时,此方法非常有用。
插入/删除成本很高,因为插入点之后的所有节点上以及所有祖先节点上都需要分别更新tw索引和sz(分支大小)值。 分支移动涉及将分支的tw值移出范围,因此在移动分支时还需要禁用外键约束。移动分支基本上需要四个查询:
调整树查询 打开/关闭树中的间隙是create/update/delete方法使用的一个重要子函数,因此我将其包括在这里。
我们首先使用一个(稍加修改的)祖先函数来更新sz值。
然后,我们需要调整tw,以使其tw高于要移除的分支。
然后,我们需要调整那些pa的tw高于要移除的分支的父级。
|
|
|
8
5
这篇文章写得很快,既不美观,也不高效(加上它有很多自动信箱,可以在
这可能违反了规则,因为我正在创建自己的对象,但嘿,我这样做是为了从实际工作中转移注意力:) 这还假设在开始构建节点之前,resultSet/table已完全读入某种结构中,如果有数十万行,这不是最佳解决方案。
|
|
|
9
4
假设您知道根元素为零,下面是要输出到文本的伪代码:
|
|
|
10
3
您可以使用hashmap模拟任何其他数据结构,因此这不是一个可怕的限制。从上到下扫描,为数据库的每一行创建一个hashmap,并为每一列创建一个条目。将这些hashmap中的每一个添加到“主”hashmap,并键入id。如果任何节点具有您尚未看到的“父节点”,请在主hashmap中为其创建一个占位符条目,并在看到实际节点时进行填充。
至于是否有“更好”的方法在数据库中存储树,这取决于您将如何使用数据。我见过一些已知最大深度的系统,它们在层次结构中的每一层都使用不同的表。如果树中的级别毕竟不完全相同(顶级类别与树叶不同),那么这就很有意义了。 |
|
|
11
1
编辑:我会先将整个表读入一个数组,这样它就不会重复查询数据库。当然,如果您的桌子很大,这将不实用。 在构建结构之后,我必须对其进行深度优先遍历,并打印出HTML。
|
|
|
12
1
这样做的目的是维护一个表示树中当前位置的堆栈。对于表中的每个元素,它会弹出堆栈元素(关闭匹配的div),直到找到当前项的父项。然后,它输出该节点的开始并将其推送到堆栈。 如果要使用缩进而不是嵌套元素输出树,只需跳过print语句即可打印div,并在每个项之前打印相当于堆栈大小的若干倍的空格。例如,在Python中:
您还可以轻松地使用此方法构造一组嵌套列表或字典。
|
|
|
13
1
为了扩展Bill的SQL解决方案,您基本上可以使用平面阵列进行相同的扩展。此外,如果您的字符串都具有相同的长度,并且您的最大子级数已知(例如在二叉树中),则可以使用单个字符串(字符数组)来实现。如果你有任意数量的孩子,这会使事情变得有点复杂。。。我必须检查我的旧笔记,看看能做些什么。 然后,牺牲一点内存,特别是如果你的树是稀疏的和/或未被缓冲的,你可以通过一点索引数学,通过存储你的树,在数组中以宽度第一的方式随机访问所有字符串,就像这样(对于二叉树):
你知道你的绳子长度,你知道的 我现在正在工作,所以不能在上面花太多时间,但有兴趣的话,我可以获取一些代码来完成这项工作。 我们用它来搜索由DNA密码子组成的二叉树,这是一个建立树的过程,然后我们将树展平以搜索文本模式,当找到时,通过索引数学(从上面反转)我们得到节点。。。我们的树很少有空节点,但我们可以在一瞬间烧掉千兆字节的数据。 |
|
|
14
0
考虑将nosql工具(如neo4j)用于层次结构。 e、 g像linkedin这样的网络应用程序使用couchbase(另一种nosql解决方案) 但nosql仅用于数据集市级查询,而不用于存储/维护事务 |
|
|
Johnny T · 基于当前值的SQL合并表[重复] 1 年前 |
|
John D · 需要为NULL或NOT NULL的WHERE子句 1 年前 |
|
ojek · 如何对SQL结果进行分组和编号? 1 年前 |
|
|
senek · 如何在PL/SQL中将选择结果(列)放入数组中 1 年前 |
|
|
Sax · 规范化Google表格(第一步) 1 年前 |
|
|
Jatin · 检索卷计数的动态sql抛出错误语法错误[关闭] 1 年前 |
|
|
Andrus · 如何在sql中查找第二个匹配项 1 年前 |