4

我正在使用嵌套集(又名修改的预排序树遍历)来存储组列表,并且我试图找到一种快速的方法来为所有组生成面包屑(作为字符串,而不是表格)一次。我的数据也使用邻接列表模型存储(有触发器使两者保持同步)。

例如:

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

我希望能够有一个返回表的用户定义函数:

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

我确实有一个用户定义的函数,它以用户 ID 作为参数,并返回一个表,其中包含所有有效组的 ID,只要在查询中的某个位置

WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid))

它会起作用的。


我有一个现有的标量函数可以做到这一点,但它不适用于任何合理数量的组(2000 个组需要 >10 秒)。它以 groupid 和 userid 作为参数,并返回一个 nvarchar。它找到给定的组父母(1个查询来获取左/右值,另一个查询来查找父母),将列表限制为用户有权访问的组(使用与上面相同的 WHERE 子句,因此还有另一个查询),然后使用游标遍历每个组并将其附加到字符串,最后返回该值。

我需要一种可以快速运行的方法(例如<= 1s),即时运行。

这是在 SQL Server 2005 上。

4

6 回答 6

5

这是对我有用的 SQL,可以从树中的任何点获取“面包屑”路径。希望能帮助到你。

SELECT ancestor.id, ancestor.title, ancestor.alias 
FROM `categories` child, `categories` ancestor 
WHERE child.lft >= ancestor.lft AND child.lft <= ancestor.rgt 
AND child.id = MY_CURRENT_ID 
ORDER BY ancestor.lft

凯丝

于 2011-08-04T16:13:29.367 回答
3

行。这适用于 MySQL,而不是 SQL Server 2005。它使用带有子查询的 GROUP_CONCAT。

这应该将完整的面包屑作为单列返回。

SELECT 
 (SELECT GROUP_CONCAT(parent.name SEPARATOR ' > ')
 FROM category parent
 WHERE node.Left >= parent.Left
 AND node.Right <= parent.Right
 ORDER BY Left
 ) as breadcrumb
FROM category node
ORDER BY Left
于 2014-03-04T03:55:53.353 回答
2

如果可以,请使用路径(或者我想我听说它被称为沿袭)字段,例如:

ID   Name    ParentId  Left   Right   Path
0    Node A  0         1      12      0,
1    Node B  0         2      5       0,1,
2    Node C  1         3      4       0,1,2,
3    Node D  0         6      11      0,3,
4    Node E  3         7      8       0,3,4,
5    Node F  4         9      9       0,3,4,

要仅获取节点 D 及以后(伪代码):

path = SELECT Path FROM Nodes WHERE ID = 3
SELECT * FROM Nodes WHERE Path LIKE = path + '%'
于 2009-04-30T22:27:52.033 回答
2

我修改了凯西的声明以获得每个元素的面包屑

SELECT
    GROUP_CONCAT(
        ancestor.name
        ORDER BY ancestor.lft ASC
        SEPARATOR ' > '
    ),
    child.*
FROM `categories` child
JOIN `categories` ancestor
ON child.lft >= ancestor.lft
AND child.lft <= ancestor.rgt
GROUP BY child.lft
ORDER BY child.lft

随意添加 WHERE 条件,例如

 WHERE ancestor.lft BETWEEN 6 AND 11
于 2014-10-16T23:09:39.560 回答
1

我最终做的是做一个大的连接,简单地将这个表与它本身联系起来,一遍又一遍地为每个级别。

首先我用第一级组填充一个表@topLevelGroups(如果你只有一个根,你可以跳过这一步),然后用用户可以看到的组填充@userGroups。

SELECT groupid,
   (level1 
    + CASE WHEN level2 IS NOT NULL THEN ' > ' + level2 ELSE '' END
    + CASE WHEN level3 IS NOT NULL THEN ' > ' + level3 ELSE '' END
   )as [breadcrumb]
FROM (
  SELECT g3.*
    ,g1.name as level1
    ,g2.name as level2
    ,g3.name as level3
  FROM @topLevelGroups g1
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid
  INNER JOIN @userGroups g3 ON g3.parentid = g2.groupid 

  UNION

  SELECT g2.*
    ,g1.name as level1
    ,g2.name as level2
    ,NULL as level3
  FROM @topLevelGroups g1 
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid

  UNION

  SELECT g1.*
    ,g1.name as level1
    ,NULL as level2
    ,NULL as level3 
  FROM @topLevelGroups g1

) a
ORDER BY [breadcrumb]

这是一个相当大的 hack,并且显然仅限于一定数量的级别(对于我的应用程序,我可以选择一个合理的限制),问题是支持的级别越多,它的连接数量就会成倍增加,因此要慢得多。

用代码做这件事肯定更容易,但对我来说,这并不总是一种选择——有时我需要直接从 SQL 查询中获得它。


我接受这个作为答案,因为这是我最终做的,它可能对其他人有用——但是,如果有人能想出一个更有效的方法,我会把它改给他们。

于 2009-08-14T20:49:14.200 回答
0

没有特定于 sql server 的代码,但您只是在寻找:

SELECT * FROM table WHERE left < (currentid.left) AND right > (currentid.right)

于 2009-04-30T22:28:54.653 回答