是否可以按物化路径树path
文本字段进行排序,以便找到树的最右侧节点?例如,考虑这个使用django-treebeard' s MP_Node
的python函数:
def get_rightmost_node():
"""Returns the rightmost node in the current tree.
:rtype: MyNode
"""
# MyNode is a subclass of django-treebeard's MP_Node.
return MyNode.objects.order_by('-path').first()
从我的所有测试中,它似乎都回归了我的期望,但我不知道如何提出数学来证明它。而且我还没有找到关于在物化路径树上执行此操作的任何信息。
Treebeard的实现在路径中没有分隔符,所以路径也是如此
如下所示:0001
,00010001
,000100010012
等
答案 0 :(得分:4)
简短回答:不。
Here is a SQLFiddle展示了我在评论中描述的问题。
对于这个简单的设置:
id, path
1, '1'
2, '1\2'
3, '1\3'
4, '1\4'
5, '1\5'
6, '1\6'
7, '1\7'
8, '1\8'
9, '1\9'
10, '1\10'
尝试使用简单排序获取最右边的叶子(id = 10
)将失败:
SELECT TOP 1
id,
path
FROM hierarchy
ORDER BY path DESC
返回:
id, path
9, 1\9
由于path
是基于文字的列,1\10
将在 1\9
之后以降序排序(请参阅第二个查询的结果小提琴)。
即使您开始追踪深度和路径长度,这通常很便宜且易于跟上,但完全有可能获得这样的路径:
path depth length
12\3\11\2 4 9
5\17\10\1 4 9
仍然无法正确排序。
即使您使用的是字母而不是数字,这只会将问题视角推向第26个孩子而不是第10个孩子:
我对物化路径操作并不像嵌套集和邻接列表那样熟悉,并且没有使用django的经验,所以如果有一些我不知道的方法,我会推迟别人,但你几乎要当然必须在path
列上执行某种解析才能始终获得正确的叶子。
编辑 - 解决了排序是否是一个有效的解决方案的问题,经过一些讨论和对问题的思考后,这里有一些关于其他潜在解决方案的补充说明:
- 当节点可以有两个以上的子节点时,“最右边”是一个模糊的术语(即,树不是二叉树)。如果一个节点有10个子节点,它们位于父节点的左侧,哪些节点位于右侧?您必须先定义此条件,然后才能定义问题的解决方案。
- 为您的问题空间正确定义“最右侧”,了解最右侧的节点不一定位于树的最低层:
1
/ \
1\1 1\2 <= This is the rightmost node
/
1\1\1 <= This is the lowest node
- 定义“最右边”,可以使用一个简单的循环以编程方式找到最右边的节点:
//in pseudocode
function GetRightmostNode(Node startNode)
{
Node currentNode = startNode;
while(currentNode.RightChildren != null)
{
currentNode = maximum of currentNode.RightChildren;
}
return currentNode;
}
此循环将查找当前节点右侧当前节点的子节点。如果它们存在,它会选择最右边的孩子并重复。一旦它到达一个右边没有子节点的节点,它就会返回当前节点,因为它找到了以startNode
为根的树(或子树)最右边的节点。
答案 1 :(得分:3)
是否可以按物化路径树的路径文本字段进行排序,以便找到树的最右侧节点?
没有。例如,如果节点路径存储为'/1/3/6/2'
,请考虑:
/1
/1/3
/1/3/6/2
/1/3/6/5
/1/3/6/21
/1/40
请参阅保罗的答案,理由将上述内容排除在外。
尽管如此,所有希望都没有失去。如果你正在搜索&#34;最右边的节点&#34;,我假设你是指树中最深的节点,你可以简单地计算分隔符。例如:
select length(regexp_replace('/1/3/6/2', '[^/]+', '', 'g')) as depth;
如果您正在寻找最大值,请使用以下内容:
order by length(regexp_replace(path, '[^/]+', '', 'g')) desc
...或等效的python代码。索引选项包括索引相同的表达式,或将结果存储在单独的深度字段中并对其进行索引。
如果您仍然对ID的实际值感兴趣,则上面的数字通常对应于ID,因此请进一步使用该列。如果它们不同,则使用不同的正则表达式提取最右边的数字,并将其转换为整数,以便自然地对它们进行排序(1,11,2),而不是按字典顺序排列(1,11,2): / p>
select regexp_replace('/1/3/6/2', '^.+/', '')::int as value;
答案 2 :(得分:0)
使用两个排序条件,一个用于深度,然后再一个用于转换为整数的最左侧节点的值:
SELECT path,
length(regexp_replace(path, '[^/]+', '', 'g')) as depth,
regexp_replace(path, '^.*/', '')::int as last
FROM test
ORDER BY depth DESC, last DESC;
这会将最高值的最深节点放在顶部。
答案 3 :(得分:-1)
您可以使用@Paul解释的方法进行一些修改。您可以在每个数字前面附加0
,并且每条路径的长度都可以统一。
可以将节点分配为路径
id | path
-----------------
1 | '01'
2 | '01\01'
3 | '01\02'
4 | '01\03'
5 | '01\04'
6 | '01\04\01'
7 | '01\04\02'
8 | '01\04\03'
9 | '01\05\01'
10 | '01\05\02'
11 | '01\05\03'
12 | '01\05\04'
如果具有最大子节点数的节点的子节点数小于100,则可以使用上面的示例。
如果它介于100和1000之间,那么您可以像0
那样添加额外的001\003\002\005
。
然后,您可以获得最正确的节点12
as,
SELECT TOP 1 id
FROM tree
ORDER BY path DESC
你可以在这里找到演示。 Demo