| Author |
Topic |
|
hasanali00
Posting Yak Master
207 Posts |
Posted - 2006-06-01 : 05:01:21
|
I have a table that holds parent and child relations, like:Parent ID Child ID10 88 77 28 66 2 Now for a given Child ID, I want to return the full path that is take to reach that Child ID. For instance, if Child ID is 2, I would get:10 > 8 > 7 > 28 > 6 > 2Any idea what I can do work around this problem?Looking forward to hearing from you.regards |
|
|
chiragkhabaria
Master Smack Fu Yak Hacker
1907 Posts |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-01 : 05:35:10
|
quote: Originally posted by hasanali00 I have a table that holds parent and child relations, like:Parent ID Child ID10 88 77 28 66 2 Now for a given Child ID, I want to return the full path that is take to reach that Child ID. For instance, if Child ID is 2, I would get:10 > 8 > 7 > 28 > 6 > 2
[code]This is not right10 > 8 > 7 > 28 > 6 > 2Since 8 is a child of 10 the rows will look like10 > 8 > 7 > 210 > 8 > 6 > 2It looks like bad design to start with, since the table design allows circular reference! |
 |
|
|
hasanali00
Posting Yak Master
207 Posts |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-01 : 06:18:56
|
quote: Originally posted by hasanali00Peso, you are 100% correct.
Is this table allowing multiple parent and multiple child? |
 |
|
|
hasanali00
Posting Yak Master
207 Posts |
Posted - 2006-06-01 : 06:28:23
|
| Yes, multiple parent and multiple child |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-01 : 09:08:17
|
| Piece of cake, NOT!Anyway, here is the code!------------------------------------------------------------------------------------------- Prepare test dataDECLARE @Nodes TABLE (Parent INT, Child INT)INSERT @Nodes (Parent, Child)SELECT 10, 8 UNION ALLSELECT 8, 7 UNION ALLSELECT 7, 2 UNION ALLSELECT 8, 6 UNION ALLSELECT 6, 2-- Keep track of path and linksDECLARE @Paths TABLE (Generation INT, Parent INT, Child INT)DECLARE @Child INT, @Generation INT-- Set up start conditionSELECT @Child = 2, @Generation = 0-- Insert the child/children to start withINSERT @Paths (Generation, Parent, Child)SELECT 0, Parent, @ChildFROM @NodesWHERE Child = @Child-- Iterate and get all parents until there is none-- IMPORTANT!!! Avoid circular reference!WHILE @@ROWCOUNT > 0 BEGIN SELECT @Generation = @Generation + 1 INSERT @Paths (Generation, Parent, Child) SELECT @Generation, Parent, Child FROM @Nodes WHERE Child IN (SELECT Parent FROM @Paths WHERE Generation = @Generation - 1) AND Parent NOT IN (SELECT Child FROM @Paths) ENDDECLARE @Stage TABLE(Path VARCHAR(8000), Parent INT, Child INT)DECLARE @Temp TABLE(Path VARCHAR(8000), Parent INT, Child INT)SELECT @Child = MAX(Generation), @Generation = 1FROM @PathsINSERT @StageSELECT CONVERT(VARCHAR, Parent) + ' > ' + CONVERT(VARCHAR, Child), Parent, ChildFROM @Paths WHERE Generation = 0WHILE @Generation <= @Child BEGIN DELETE FROM @Temp INSERT @Temp SELECT CONVERT(VARCHAR, p.Parent) + ' > ' + s.Path, p.Parent, s.Parent FROM @Stage s LEFT JOIN ( SELECT Parent, Child FROM @Paths WHERE Generation = @Generation ) p ON p.Child = s.Parent IF (SELECT COUNT(*) FROM @Temp WHERE Path IS NULL) > 0 BEGIN DELETE FROM @Temp WHERE Path IS NULL INSERT @Temp SELECT Path, Parent, Child FROM @Stage END DELETE FROM @Stage INSERT @Stage SELECT Path, Parent, Child FROM @Temp SELECT @Generation = @Generation + 1 END-- Output the pathsSELECT PathFROM @StageORDER BY LEN(Path), REVERSE(Path)-----------------------------------------------------------------------------------------Enjoy! |
 |
|
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-06-01 : 10:48:02
|
I believe the easiest way to deal with multiple parents is to start at the bottom of the tree (nodes with no children) and work your way up.I wrote an example the other day... I'll see if I can find it.be back!Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-06-01 : 10:55:39
|
I found it.... look at the bottom of this thread: http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=66466Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
|
RyanRandall
Master Smack Fu Yak Hacker
1074 Posts |
Posted - 2006-06-02 : 11:39:44
|
Hi all,Here's my stab at something simple...  --dataDECLARE @Nodes TABLE (Parent INT, Child INT)INSERT @Nodes (Parent, Child)SELECT 10, 8 UNION ALLSELECT 11, 7 UNION ALL --for multiple top nodesSELECT 8, 7 UNION ALLSELECT 7, 8 UNION ALL --for infinite loopSELECT 7, 2 UNION ALLSELECT 8, 6 UNION ALLSELECT 6, 2--inputdeclare @Child intset @Child = 2--calculationdeclare @NodePaths table (Path varchar(8000), TopNode int)insert @NodePaths select @Child, @Childwhile @@rowcount > 0 insert @NodePaths select cast(Parent as varchar(10)) + ' > ' + p.Path, Parent from @NodePaths p inner join @Nodes n on n.Child = p.TopNode where charindex(' > ' + cast(Parent as varchar(10)) + ' > ', ' > ' + Path + ' > ') = 0 --prevent going round in circles and cast(Parent as varchar(10)) + ' > ' + p.Path not in (select Path from @NodePaths) --prevent redoing paths already doneselect Path from @NodePaths where TopNode not in (select Child from @Nodes) order by Path/*resultsPath ---------------------10 > 8 > 6 > 210 > 8 > 7 > 211 > 7 > 211 > 7 > 8 > 6 > 2*/Ryan Randallwww.monsoonmalabar.com London-based IT consultancy Solutions are easy. Understanding the problem, now, that's the hard part. |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-02 : 15:00:35
|
quote: while @@rowcount > 0 insert @NodePaths select cast(Parent as varchar(10)) + ' > ' + p.Path, Parent from @NodePaths p inner join @Nodes n on n.Child = p.TopNode where charindex(' > ' + cast(Parent as varchar(10)) + ' > ', ' > ' + Path + ' > ') = 0 --prevent going round in circles and cast(Parent as varchar(10)) + ' > ' + p.Path not in (select Path from @NodePaths) --prevent redoing paths already done
Excellent! Sometimes I think you're a wizard... |
 |
|
|
RyanRandall
Master Smack Fu Yak Hacker
1074 Posts |
Posted - 2006-06-05 : 05:21:26
|
quote: Excellent! Sometimes I think you're a wizard...
The feeling is mutual, Peso... Ryan Randallwww.monsoonmalabar.com London-based IT consultancy Solutions are easy. Understanding the problem, now, that's the hard part. |
 |
|
|
hasanali00
Posting Yak Master
207 Posts |
Posted - 2006-06-05 : 10:24:36
|
Thanks everyoneYes, Ryan's solution works very well.Prior to Ryan's solution, I was working on my own and very limited solution.I defined a temp table with 5 columns, like:declare @tempID intcreate Table #ParentCategories (id int identity(1,1), category5 int, category4 int , category3 int, category2 int , category1 int)insert into #ParentCategories (category5, category4)select @ChildID, ParentID from CategoryRelations where childID = @ChildID-- UPDATE CATEGORY 3 select @tempID = ParentID from CategoryRelations C , #ParentCategories P where c.childID = P.category4 and category5 = select childid from CategoryRelations , #ParentCategories where parentid = CATEGORY4 My logic is to update #ParentCategories.Category3 with the parentID of #ParentCategories.Category4 WHERE #ParentCategories.Category5 is the ChildID of #ParentCategories.Category4I think the logic is OK, but I could not get the SQL to work.Just out of interest, if I was carrying on with my approach, how can I make my SQL work. Kind regards |
 |
|
|
sqldev80
Yak Posting Veteran
68 Posts |
Posted - 2006-06-16 : 11:47:23
|
| This code find out the parent of the child. If I want to change it and want to enter the parent and find all the children of the parent how would that be done.so result would be in opposite way and will show all the childrenonce the parent id is input2 < 6 < 8 < 102 < 7 < 8 < 102 < 7 < 112 < 6 < 8 < 7 < 11DECLARE @Nodes TABLE (Parent INT, Child INT)INSERT @Nodes (Parent, Child)SELECT 10, 8 UNION ALLSELECT 11, 7 UNION ALL --for multiple top nodesSELECT 8, 7 UNION ALLSELECT 7, 8 UNION ALL --for infinite loopSELECT 7, 2 UNION ALLSELECT 8, 6 UNION ALLSELECT 6, 2--inputdeclare @Child intset @Child = 2--calculationdeclare @NodePaths table (Path varchar(8000), TopNode int)insert @NodePaths select @Child, @Childwhile @@rowcount > 0 insert @NodePaths select cast(Parent as varchar(10)) + ' > ' + p.Path, Parent from @NodePaths p inner join @Nodes n on n.Child = p.TopNode where charindex(' > ' + cast(Parent as varchar(10)) + ' > ', ' > ' + Path + ' > ') = 0 --prevent going round in circles and cast(Parent as varchar(10)) + ' > ' + p.Path not in (select Path from @NodePaths) --prevent redoing paths already doneselect Path from @NodePaths where TopNode not in (select Child from @Nodes) order by Path/*resultsPath ---------------------10 > 8 > 6 > 210 > 8 > 7 > 211 > 7 > 211 > 7 > 8 > 6 > 2 |
 |
|
|
RyanRandall
Master Smack Fu Yak Hacker
1074 Posts |
Posted - 2006-06-16 : 12:05:39
|
A few cheeky reversals will do it...--dataDECLARE @Nodes TABLE (Parent INT, Child INT)INSERT @Nodes (Parent, Child)SELECT 10, 8 UNION ALLSELECT 11, 7 UNION ALL --for multiple top nodesSELECT 8, 7 UNION ALLSELECT 7, 8 UNION ALL --for infinite loopSELECT 7, 2 UNION ALLSELECT 8, 6 UNION ALLSELECT 6, 2--inputdeclare @Parent intset @Parent = 11--calculationdeclare @NodePaths table (Path varchar(8000), EndNode int)insert @NodePaths select @Parent, @Parentwhile @@rowcount > 0insert @NodePathsselect cast(Child as varchar(10)) + ' < ' + p.Path, Childfrom @NodePaths p inner join @Nodes n on n.Parent = p.EndNodewhere charindex(' < ' + cast(Child as varchar(10)) + ' < ', ' < ' + Path + ' < ') = 0 --prevent going round in circlesand cast(Child as varchar(10)) + ' < ' + p.Path not in (select Path from @NodePaths) --prevent redoing paths already doneselect Path from @NodePaths where EndNode not in (select Parent from @Nodes) order by Path/*resultsPath -------------------2 < 6 < 8 < 7 < 112 < 7 < 11*/Ryan Randallwww.monsoonmalabar.com London-based IT consultancy Solutions are easy. Understanding the problem, now, that's the hard part. |
 |
|
|
sqldev80
Yak Posting Veteran
68 Posts |
Posted - 2006-06-16 : 12:37:51
|
| Thanks Ryan,I more question. I have column likeParent , child1 101 1110 1212 14I do not want my data entry people at any cost enter this in the data base and make a circulare referece:Parent Child12 1010 1214 12 I do not want to break hierarchy at any cost. What is the best way to prevent it. Thanks |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-16 : 14:33:29
|
quote: Originally posted by sqldev80 Thanks Ryan,I more question. I have column likeParent , child1 101 1110 1212 14I do not want my data entry people at any cost enter this in the data base and make a circulare referece:Parent Child12 1010 1214 12 I do not want to break hierarchy at any cost. What is the best way to prevent it. Thanks
Ryan's function is well equiped to handle circulare references. Try it!Peter LarssonHelsingborg, Sweden |
 |
|
|
sqldev80
Yak Posting Veteran
68 Posts |
Posted - 2006-06-16 : 14:39:02
|
| which funtion is it? |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-16 : 16:46:43
|
quote: Originally posted by sqldev80 which funtion is it?
The two functions Ryan has written in this topic. There is a remark where he says that circular refernence is taken care of, so that the loop doesn't continue forever. And it is true. I have tried to break it. Both with first level circular reference (a -> a) and second level (a -> b -> a) and at last third level (a -> b -> c -> a)Try it for yourself.Peter LarssonHelsingborg, Sweden |
 |
|
|
sqldev80
Yak Posting Veteran
68 Posts |
Posted - 2006-06-16 : 17:06:28
|
| I know that. But i need some clause in the table which even prevents such sort of data entry He has entered that data and is taking care of it.I would like to avoid that type to data entrySo any time there is an entry where the hierarchy level is brokenit should give an error. |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-06-16 : 17:36:27
|
quote: Originally posted by sqldev80 I know that. But i need some clause in the table which even prevents such sort of data entry He has entered that data and is taking care of it.I would like to avoid that type to data entrySo any time there is an entry where the hierarchy level is brokenit should give an error.
A very easy way is to make a function that returns bit true/false if circular reference exists. then use this function as constraint for table.Peter LarssonHelsingborg, Sweden |
 |
|
|
Next Page
|