Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding breadcrumbs for nested sets

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

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.

like image 606
gregmac Avatar asked Dec 14 '22 04:12

gregmac


2 Answers

here's the SQL that worked for me to get the "breadcrumb" path from any point in the tree. Hope it helps.

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

Kath

like image 92
Kathy Keating Avatar answered Dec 15 '22 17:12

Kathy Keating


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.

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
like image 30
sorohan Avatar answered Dec 15 '22 18:12

sorohan