寻找嵌套集的面包屑
我正在使用嵌套集(又名修改的前序树遍历)来存储组列表,并且我正在尝试找到一种快速方法来一次为所有组生成面包屑(作为字符串,而不是表)。 我的数据也使用邻接列表模型存储(有触发器使两者保持同步)。
例如:
ID Name ParentId Left Right
0 Node A 0 1 12
1 Node B 0 2 5
2 Node C 1 3 4
3 Node D 0 6 11
4 Node E 3 7 8
5 Node F 4 9 9
代表树:
- 节点 A
- 节点 B
- 节点C
- 节点 D
- 节点E
- 节点F
- 节点 B
我希望能够有一个返回表的用户定义函数:
ID Breadcrumb
0 Node A
1 Node A > Node B
2 Node A > Node B > Node C
3 Node A > Node D
4 Node A > Node D > Node E
5 Node A > Node D > Node F
为了使这个稍微复杂一些(尽管这有点超出了问题的范围),我还有用户需要遵守的限制。 例如,如果我只能访问 id=3,那么当我运行查询时,我应该得到:
ID Breadcrumb
3 Node D
4 Node D > Node E
5 Node D > Node F
我确实有一个用户定义的函数,它接受 userid 作为参数,并返回一个表,其中包含所有组的 id是有效的,所以只要查询中的某个地方
WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid))
它就会起作用。
我有一个现有的标量函数可以做到这一点,但它不适用于任何合理数量的组(在 2000 个组上需要 >10 秒)。 它采用 groupid 和 userid 作为参数,并返回 nvarchar。 它找到给定的组父项(1 个查询获取左/右值,另一个查询查找父项),将列表限制为用户有权访问的组(使用与上面相同的 WHERE 子句,因此还有另一个查询),然后使用游标遍历每个组并将其附加到字符串中,最后返回该值。
我需要一种能够快速运行(例如<= 1s)的方法来执行此操作。
这是在 SQL Server 2005 上。
I'm using nested sets (aka modified preorder tree traversal) to store a list of groups, and I'm trying to find a quick way to generate breadcrumbs (as a string, not a table) for ALL of the groups at once. My data is also stored using the adjacency list model (there are triggers to keep the two in sync).
So for example:
ID Name ParentId Left Right
0 Node A 0 1 12
1 Node B 0 2 5
2 Node C 1 3 4
3 Node D 0 6 11
4 Node E 3 7 8
5 Node F 4 9 9
Which represents the tree:
- Node A
- Node B
- Node C
- Node D
- Node E
- Node F
- Node B
I would like to be able to have a user-defined function that returns a table:
ID Breadcrumb
0 Node A
1 Node A > Node B
2 Node A > Node B > Node C
3 Node A > Node D
4 Node A > Node D > Node E
5 Node A > Node D > Node F
To make this slightly more complicated (though it's sort of out of the scope of the question), I also have user restrictions that need to be respected. So for example, if I only have access to id=3, when I run the query I should get:
ID Breadcrumb
3 Node D
4 Node D > Node E
5 Node D > Node F
I do have a user-defined function that takes a userid as a parameter, and returns a table with the ids of all groups that are valid, so as long as somewhere in the query
WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid))
it will work.
I have an existing scalar function that can do this, but it just does not work on any reasonable number of groups (takes >10 seconds on 2000 groups). It takes a groupid and userid as a parameter, and returns a nvarchar. It finds the given groups parents (1 query to grab the left/right values, another to find the parents), restricts the list to the groups the user has access to (using the same WHERE clause as above, so yet another query), and then uses a cursor to go through each group and append it to a string, before finally returning that value.
I need a method to do this that will run quickly (eg. <= 1s), on the fly.
This is on SQL Server 2005.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是帮助我从树中的任何点获取“面包屑”路径的 SQL。 希望能帮助到你。
凯丝
here's the SQL that worked for me to get the "breadcrumb" path from any point in the tree. Hope it helps.
Kath
好的。 这是针对 MySQL 的,而不是 SQL Server 2005。它使用带有子查询的 GROUP_CONCAT。
这应该将完整的面包屑作为单列返回。
Ok. This is for MySQL, not SQL Server 2005. It uses a GROUP_CONCAT with a subquery.
This should return the full breadcrumb as single column.
如果可以的话,请使用路径(或者我想我听说过它被称为谱系)字段,例如:
仅获取节点 D 及之后的节点(伪代码):
If you can, use a path (or I think I've heard it referred as a lineage) field like:
To get just node D and onward (psuedocode):
我修改了 Kathy 的声明以获得每个元素的面包屑
随意添加 WHERE 条件,例如
I modified the Statement of Kathy to get breadcrumbs for every element
Feel free to add a WHERE condition e.g.
我最终做的是创建一个大型连接,简单地将这个表与其自身联系起来,对于每个级别一遍又一遍。
首先,我仅使用第一级组填充表 @topLevelGroups(如果您只有一个根,则可以跳过此步骤),然后使用用户可以看到的组填充 @userGroups。
这是一个相当大的黑客,并且显然仅限于一定数量的级别(对于我的应用程序,我可以选择一个合理的限制),问题是支持的级别越多,它就会成倍地增加连接数量,因此速度要慢得多。
在代码中执行此操作肯定更容易,但对我来说,这并不总是一个选项 - 有时我需要直接从 SQL 查询中获得此功能。
我接受这个作为答案,因为这是我最终所做的,并且可能对其他人有用——但是,如果有人能想出更有效的方法,我会将其更改为他们。
What I ended up doing is making a large join that simply ties this table to itself, over and over for every level.
First I populate a table @topLevelGroups with just the 1st level groups (if you only have one root you can skip this step), and then @userGroups with the groups that user can see.
This is a pretty big hack, and is obviously limited to a certain number of levels (for my app, there is a reasonable limit I can pick), with the problem that the more levels are supported, it increases the number of joins exponentially, and thus is much slower.
Doing it in code is most certainly easier, but for me that is simply not always an option - there are times when I need this available directly from a SQL query.
I'm accepting this as the answer, since it's what I ended up doing and it may work for other people -- however, if someone can come up with a more efficient method I'll change it to them.
没有 sql server 特定代码,但您只是在寻找:
SELECT * FROM table WHERE left < (currentid.left) AND 右 > (当前id.右)
no sql server specific code, but are you simply looking for :
SELECT * FROM table WHERE left < (currentid.left) AND right > (currentid.right)