是否有可能查询一个单一的查询在MySQL中的树形结构表,到任何深度?
我想答案是否定的,但我很喜欢它有人有任何洞察到如何抓取一个树结构到SQL(MySQL)的任何深度,但与一个单一的查询
更具体地说,给定一个树形结构的表(id,data,data,parent_id)和表中的一行,是否有可能获得所有的后代(子/孙/ etc),或所有的祖先(父母/祖父母/等),而不知道它会走多远,使用一个单一的查询?
或者正在使用某种recursion要求,在那里我一直查询更深,直到没有新的结果?
具体来说,我使用Ruby和Rails,但我猜这不是很相关。
是的,这是可能的,这是一个被称为修改预定树遍历,这里最好的描述
Joe Celko在SQL中用于Smarties的树和层次结构
这里提供了一个工作示例(在PHP中)
http://www.sitepoint.com/article/hierarchical-data-database/2/
这里有几个资源:
- http://forums.mysql.com/read.php?10,32818,32818#msg-32818
- 在MySQL中pipe理分层数据
- http://lists.mysql.com/mysql/201896
基本上,你需要在存储过程或查询中做某种forms的游标,或者build立一个邻接表。 我会避免在db之外recursion:取决于你的树有多深,这可能会非常缓慢/粗略。
当你问的主要问题是“我的孩子是什么”和“我的父母是什么”时,Daniel Beardsley的回答并不是什么糟糕的解决scheme。
为了回应Alex Weinstein,这个方法实际上导致父节点上的节点更新比Celko技术更less。 在Celko的技术中,如果最左边的2级节点移动到最右边的1级节点下,那么几乎树中的每个节点都需要更新,而不仅仅是节点的子节点。
然而,我想说的是,丹尼尔可能把path存回错误的方向。
我会存储他们,这样的查询将是
SELECT FROM table WHERE ancestors LIKE "1,2,6%"
这意味着mysql可以使用'ancestors'列上的索引,而不能使用前导%。
我之前遇到过这个问题,有一个古怪的想法。 您可以在每个logging中存储一个字段,将其直接祖先的ID的string连接到根。
想象一下,你有这样的logging(缩进意味着heirarchy和数字是id,祖先。
- 1,“1”
- 2,“2,1”
- 5,“5,2,1”
- 6,“6,2,1”
- 7,“7,6,2,1”
- 11,“11,6,2,1”
- 3,“3,1”
- 8,“8,3,1”
- 9,“9,3,1”
- 10,“10,3,1”
- 2,“2,1”
然后selectid:6的后代 ,就这样做
SELECT FROM table WHERE ancestors LIKE "%6,2,1"
保持祖先专栏的更新可能比您值得的更麻烦,但在任何数据库中都是可行的解决scheme。
Celko的技术(嵌套)非常好。 我还使用了一个与“祖先”,“后代”和“距离”相关的邻接表(例如,直接的孩子/父母距离为1,孙子/祖父母距离为2等)。
这需要被维护,但插入操作相当容易:使用事务,然后将直接链接(parent,child,distance = 1)放入表中,然后通过添加距离来插入现有父项和子项的select当我有机会时,我可以拉起SQL),它需要在3个字段中的每一个上进行索引来获得性能。 这种方法变得丑陋的地方是删除…你基本上必须标记所有已经受到影响的项目,然后重build它们。 但是这样做的好处是它可以处理任意的非循环图,而嵌套集模型只能做直线层次结构(例如除了根以外的每个项都只有一个父项)。
SQL不是一个图灵完成语言,这意味着你不能够执行这种循环。 你可以用SQL和树结构来做一些非常聪明的事情,但是我想不出一种方法来描述一个具有任意深度层次结构的“在它的层次结构中”的特定id的行。
你最好的select是根据@Danbuild议的方式,就是用其他更有用的语言在树上工作。 实际上,您可以使用循环在通用语言中生成一个查询string,其中查询只是一些复杂的连接(或子查询),反映了您正在寻找的层次结构的深度。 这比循环和多个查询更有效率。
这绝对可以完成,对于SQL来说并不复杂。 我已经回答了这个问题,并在这里提供了一个使用mysql程序代码的工作示例:
MySQL:如何在特定节点中find叶子
展位:如果您满意,您应该将其中一个答案标记为已接受。
我使用了https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql (由https://stackoverflow.com/users/1726419/yossico提供)中描述的“With Emulator”例程。 到目前为止,我已经得到了非常好的结果(性能明智),但是我没有大量的数据或大量的后代来search。
你几乎肯定会想用这个recursion。 如果你这样做,那么把整棵树而不是一点一点地固定到一个固定的深度将是微不足道的(事实上更容易)。
在非常粗略的伪代码中,您需要沿着这些方向行事:
getChildren(parent){ children = query(SELECT * FROM table WHERE parent_id = parent.id) return children } printTree(root){ print root children = getChildren(root) for child in children { printTree(child) } }
虽然在实践中你很less想要做这样的事情。 这将是相当低效的,因为它正在对表中的每一行发出一个请求,所以对于小表或者没有嵌套得太深的树来说只是明智的。 说实话,在任何情况下,你可能都想限制深度。
但是,考虑到这些数据结构的stream行,很可能会有一些MySQL的帮助,特别是减less你需要查询的数量。
编辑:有这个想法,做所有这些查询是没有意义的。 如果你正在读整个表,那么你可以把所有东西都写入RAM中 – 假设它足够小!