Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 SQL Server Development (2000)
 Hierachy and Bread Crumbs

Author  Topic 

timgaunt
Posting Yak Master

115 Posts

Posted - 2005-09-07 : 13:19:28
Hi,

I know I've asked the same question in the articles discussion forum but I wonder if it was the wrong place :) (sorry if its not)

I've been using Corey's Tree Hierachy code (http://www.seventhnight.com/treestructs.asp) but have got a little stuck extracing the data.

Basically we're trying to achieve two main outputs, the first being a breadcrumb navigation system to thier current location ie:
England > West Midlands > Dudley

The second is a list of areas within an area ie (for Dudley):
- Brierley Hill
- Merry Hill

The data we're using looks like this (PlaceID, ParentID, PlaceName):

1 0 England
2 1 West Midlands
3 2 Dudley
4 3 Brierley Hill
5 3 Merry Hill

Is the method described by Corey the best to achieve this? I think the issue is that I don't need SQL to concatenate the strings as I can manage that in ASP.NET but its how to extract each Parent as a seperate record...

Cheers.

Tim

Kristen
Test

22859 Posts

Posted - 2005-09-07 : 14:16:33
Just to make sure I've understood:

Given "Dudley" [level 2] you want to find the ancestors ("West Midlands", and then "England" - [Level 1 and Level 0]) and All Children ("Brierley Hill" and "Merry Hill") [Level 3]?

Kristen
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2005-09-07 : 14:16:58
Ok, after a little more playing I've worked out how to get the info in the way required but is it correct or is it overly server intensive, basically what I'm doing after it builds the paths table is selecting the record from the paths table with the NodeID (PlaceID) we're interested in and then splitting its path and joining that to all the name at the end, is there a better way of doing that?

Here's my code which replaces Corey's final select statement:

Declare @nodePath nvarchar(1000)
SELECT @nodePath = SUBSTRING(path, 1 , LEN(path)-1) --Trim off the final ;
FROM @paths
WHERE PlaceID = @PlaceID --Passed into the SProc (its an INT)

CREATE TABLE #List(Item varchar(8000)) -- Create a temporary table

DECLARE @Delimiter char(1)
DECLARE @Item Varchar(8000)
SET @Delimiter = ';' -- delimiter that separates items

WHILE CHARINDEX(@Delimiter, @nodePath,0) <> 0
BEGIN
SELECT
@Item=RTRIM(LTRIM(SUBSTRING(@nodePath,1,CHARINDEX(@Delimiter, @nodePath,0)-1))),
@nodePath=RTRIM(LTRIM(SUBSTRING(@nodePath,CHARINDEX(@Delimiter, @nodePath,0)+1,LEN(@nodePath))))

IF LEN(@nodePath) > 0
INSERT INTO #List
SELECT @Item

END

IF LEN(@nodePath) > 0
INSERT INTO #List SELECT @nodePath -- Put the last item in

/* Select all the new ID's and join to get the names etc */
SELECT
*
FROM #List As A INNER JOIN @tree As B
ON CAST(A.Item As int) = B.PlaceID

DROP TABLE #List
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2005-09-07 : 14:18:21
Hi Kristen,

Yeah thats basically it but its two seperate datasets, so one will be to find the parents and the other to find the children, I've sorted the children as thats in Corey's article but its just the ancestors that I'm having problems with :).

The reason I was questioning the children was because I'm wondering if this is overkill for our requirements -in this example we won't have many parent nodes to a single child node.

Tim
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2005-09-07 : 14:30:58
I haven't reviewed the article, but we do two things

1) Store a "path", in each record, of all the ancestors (i.e. a delimited, or fixed length, list of IDs or somesuch)

2) Store a Sequence Number so that an ORDER BY will give us the whole tree (or some part of it) in "Tree Order"

But that may be out of context without me having read the article.

Krsten
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2005-09-07 : 16:42:20
sorry... didn't read that it had moved...:

here's my post from yonder

quote:

Well I'll be darned!!

@nodes is a reference to a distinct list of nodes. In a multiparent environment, node definition must exist in a table seperate from node relationships. In a single parent environment, the relationship can be located on the node definition record.

Since your table contains the relationship as well as the definition, you can replace @nodes with your table name...

Here's my shot:


Declare @tree table
(
cNodeId int,
pNodeId int,
name varchar(50),
processed bit default(0)
)
Insert Into @tree
Select 1, 0, 'England', 0 Union All
Select 2, 1, 'West Midlands', 0 Union All
Select 3, 2, 'Dudley', 0 Union All
Select 4, 3, 'Brierley Hill', 0 Union All
Select 5, 3, 'Merry Hill', 0


Declare @pad nvarchar(100),
@lastCnt int

Set @Pad = '0000'

Declare @paths table (path nvarchar(1000), pNodeId int, cNodeId int)

Insert Into @paths
Select
path=right(@pad + convert(nvarchar,pNodeId),len(@pad))+';' + right(@pad + convert(nvarchar,cNodeId),len(@pad))+';',
pNodeId,
cNodeId
From @Tree where pNodeId=0

Update A
Set Processed = 1
From @Tree as A
Inner Join @paths as B
On A.pNodeId = B.pNodeId
and A.cNodeId = B.cNodeId

While exists(Select * From @tree Where Processed = 0) Begin Insert Into @paths
Select
path=path + case when B.cNodeId is not null then right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';' else '' end,
B.pNodeId,
B.cNodeId
From @Paths as A
Left Join @Tree as B
On A.cNodeId = B.pNodeId
Where B.Processed = 0

Update A
Set Processed = 1
From @Tree as A
Inner Join @paths as B
On A.pNodeId = B.pNodeId
and A.cNodeId = B.cNodeId
Where A.Processed = 0
End

Select * From @paths

Declare @nodeId int
Set @nodeId = 3

Select distinct pNodeId = B.cNodeId, B.name, level = (charindex(right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';',A.Path)-1)/(len(@pad)+1)
From @paths as A, @Tree as B
Where A.path like
'%'+right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';%'+right(@pad+convert(nvarchar,@NodeId),len(@pad))+';%'


Select distinct A.cNodeId, B.Name, level = (charindex(right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';',A.Path)-1)/(len(@pad)+1)
From @paths as A
Inner Join @tree as B
On A.cNodeId = B.cNodeId
Where A.path like '%' + right(@pad + convert(nvarchar,@NodeId),len(@pad))+';%'
and A.cNodeId <> @NodeId


I'm not sure exactly what you were going for with the children section... I would really just suggest the path table minus the current path... but let me know where you're at!?



Corey

Co-worker on The Wizard of Oz "...those three midgets that came out and danced, the freaked me out when I was little. But they are ok now."
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2005-09-08 : 05:27:20
Hi Corey,

thanks for your reply, I decided to move it here as although you should be crowned the King of Hierachial Tree data I felt it was unfair to expect you to reply.

Thanks also to Kristen for the response but sadly I can't use order by because it also brings through other records that will distort the info and the entire path is stored in the table but didn't get me very far.

In regards your code Corey, it does indeed produce the correct results but its incredibly slow (10seconds for 1086 records) which is odd because the path's code runs fine, when I use the code that I posted a couple of posts ago only takes 1/2seconds so I guess its the way to go or have I missed som glaring error?

Re: the children, thanks I sorted that, not too sure what I was asking now lol, I think it was just so you had my entire problem lol.

Many thanks again.

Tim
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2005-09-08 : 08:27:11
I'm glad you got it figured out!

I have a different query for the parents, so if you get a chance would you test it out and see if it does better than the 10 seconds for 1086 rows??


replace:
Select distinct pNodeId = B.cNodeId, B.name, level = (charindex(right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';',A.Path)-1)/(len(@pad)+1)
From @paths as A, @Tree as B
Where A.path like
'%'+right(@pad+convert(nvarchar,B.cNodeId),len(@pad))+';%'+right(@pad+convert(nvarchar,@NodeId),len(@pad))+';%'


with:

Select distinct pNodeId = C.cNodeId, C.name, Level = (len(A.Path)/(len(@pad)+1))-1
From @paths as A
Inner Join (Select path from @paths Where cNodeId=@NodeId) B
On B.path like A.path+'%'
Inner Join @Tree as C
On A.cNodeId = C.cNodeId
Where C.cNodeId<>@NodeId




Corey

Co-worker on The Wizard of Oz "...those three midgets that came out and danced, the freaked me out when I was little. But they are ok now."
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2005-09-08 : 10:03:39
Super Corey thats much better and I much prefer yours thanks for that. You can rightly claim your Hierachy Crown again haha. The only change I made was remove "Where C.cNodeId<>@NodeId" so I could also have the most current node for the breadcrumb (can't have these people getting lost now can we haha)

Thanks again

Tim
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2005-09-08 : 10:24:59
Why thank you
...
and you're welcome!!

Corey

Co-worker on The Wizard of Oz "...those three midgets that came out and danced, the freaked me out when I was little. But they are ok now."
Go to Top of Page
   

- Advertisement -