有没有一种简单的方法来查询节点的子节点?
Posted
技术标签:
【中文标题】有没有一种简单的方法来查询节点的子节点?【英文标题】:Is there a simple way to query the children of a node? 【发布时间】:2010-10-14 03:44:38 【问题描述】:我最近一直在使用Nested Set Model 中的废话。我喜欢为几乎所有有用的操作和视图设计查询。我坚持的一件事是如何选择节点的直接子节点(仅子节点,而不是进一步的后代!)。
老实说,我确实知道一种方法 - 但它涉及无法管理的 SQL 数量。我确信有一个更直接的解决方案。
【问题讨论】:
【参考方案1】:您是否阅读了您发布的文章?它在“查找节点的直接下属”标题下
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft;
但是,我所做的(这是作弊)是将嵌套集与邻接列表结合在一起——我在表中嵌入了一个“parent_id”,这样我就可以轻松地询问一个节点的子节点。
【讨论】:
"...我将嵌套集与邻接列表组合在一起..."哈!这就是我正在做的事情。我加入了一个 adj。列表视图,基于 Joe Celko 的查询。这似乎是一大堆代码。甚至链接文章的解决方案也是......冗长。 我的意思是,比较选择所有节点的后代:SELECT * FROM nodes WHERE nodes.leftBound BETWEEN parentLeftBound AND parentRightBound; 好吧,“child_view”很简单,SELECT * FROM nodes WHERE parent_id = 123456 :D +1 表示“作弊”。通常,当使用嵌套集模型时,查询时间比建表更受关注。我发现添加一些其他的东西,比如 ParentId,甚至 Depth 在某些情况下都非常有用,并且添加到表构建中的成本并不高。混合嵌套集邻接列表表很棒。【参考方案2】:在我看来,如果没有子查询或父列冗余,这应该很容易实现!例如,给定父母的左右是已知的:
SELECT child.id
FROM nodes AS child
LEFT JOIN nodes AS ancestor ON
ancestor.left BETWEEN @parentleft+1 AND @parentright-1 AND
child.left BETWEEN ancestor.left+1 AND ancestor.right-1
WHERE
child.left BETWEEN @parentleft+1 AND @parentright-1 AND
ancestor.id IS NULL
也就是说,“从所讨论节点的所有后代中,选择它们与节点之间没有祖先的那些”。
【讨论】:
我想知道哪个答案在性能上更好,这个答案还是接受的帖子。但是,这两种解决方案都有效。这个看起来更紧凑一些。 对于非常大的树,我们发现这在 mysql 中表现不佳,在 SQL Server 中表现更差,因为它需要在数据库上执行嵌套循环。我们将代码更改为只检索所有后代,然后在我们的应用程序代码中只修剪到子代。 @andreas 这与接受的答案非常相似,不同之处在于它不是计算孩子并过滤到有 1 个孩子的孩子,而是通过查看祖先是否为 NULL 来过滤。这意味着它有更少要做的工作(没有排序和计数步骤)。它应该更快,但我没有测试过。 @user393274 您的答案似乎是在回答 andreas(比较方法),但事实并非如此。您正在回答关于一般使用 SQL 来获取孩子的问题。【参考方案3】:这个更好更小
用户“bobince”几乎拥有它。我想通了并让它为我工作,因为我比大多数人有更多的 MySQL 经验。但是,我明白为什么 bobince 的回答可能会吓跑人们。他的询问不完整。您需要先将 parent_left 和 parent_right 选择到 mysql 变量中。
下面的两个查询假设您的表名为tree
,您的左列名为lft
,右列名为rgt
,并且您的主键名为id
。更改这些值以满足您的需要。此外,检查第一个 select 语句。您会看到我正在查找节点 5 的直接后代。更改数字 5 以查找您想要的任何节点的子节点。
我个人认为这是一个比目前提出的其他查询更时尚、更性感、更有效的查询。
SELECT `lft`, `rgt` INTO @parent_left, @parent_right FROM efm_files WHERE `id` = 5;
SELECT `child`.`id`
FROM `tree` AS `child`
LEFT JOIN `tree` AS `ancestor` ON
`ancestor`.`lft` BETWEEN @parent_left+1 AND @parent_right-1 AND
`child`.`lft` BETWEEN `ancestor`.`lft`+1 AND `ancestor`.`rgt`-1
WHERE
`child`.`lft` BETWEEN @parent_left+1 AND @parent_right-1 AND
`ancestor`.`id` IS NULL
【讨论】:
efm_files 是我的 mysql 数据库中表的名称。将其替换为您自己的数据库表名。 这太棒了 - 我在 SQL 数据库中使用,你的方法就像一个冠军!【参考方案4】:我知道我在做一个死灵帖子, 但这是我的看法。
为什么不在嵌套集中包含“深度”列? 深度列将指示项目的“级别”。
所以,要选择项目的直接子项,只需这样做
select c.*
from tree as p
join tree as c on (c.left > p.left and c.right < p.right and c.depth = p.dept + 1)
where p.id = @parentID
【讨论】:
因为严格来说这不再是嵌套集合,而是层次模型的组合。而且通常情况下,改变模型来解决问题并不是一种选择。【参考方案5】:我也会选择深度列。但是使用
SELECT Child.Node, Child.LEFT, Child.RIGHT
FROM Tree AS Child, Tree AS Parent
WHERE
Child.Depth = Parent.Depth + 1
AND Child.LEFT > Parent.LEFT
AND Child.RIGHT < Parent.RIGHT
AND Parent.LEFT = 1 -- Given Parent Node Left Index
Wikipedia
【讨论】:
请注意,它需要additional深度列以及左右Id。【参考方案6】:我发现Wikipedia link 有很好的最小化答案版本以及选定的答案。
SELECT DISTINCT Child.Name
FROM ModelTable AS Child, ModelTable AS Parent
WHERE Parent.Lft < Child.Lft AND Parent.Rgt > Child.Rgt -- associate Child Nodes with ancestors
GROUP BY Child.Name
HAVING MAX(Parent.Lft) = @parentId -- Subset for those with the given Parent Node as the nearest ancestor
还有,大家尝试用Linq表达,请点击链接:https://***.com/a/25594386/361100
【讨论】:
以上是关于有没有一种简单的方法来查询节点的子节点?的主要内容,如果未能解决你的问题,请参考以下文章
sql 知道父节点,查询所有的子节点,运用游标,递归,存储过程