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.
Author |
Topic |
lianaent
Starting Member
5 Posts |
Posted - 2008-04-15 : 18:38:40
|
I struggled with this for several days, and could find no help online for this particular issue. I figured out the answer finally and thought I'd share it.The problem is how do you return all children and their siblings from a self-referencing table given a parent id. It turns out it's really simple using recursion. It may be simpler in SQL 2005 or 2008, but I don't have those.CREATE FUNCTION f_GetChildren (@ParentId int) RETURNS @tblTree TABLE ( ParentId int, ChildId int )ASBEGIN DECLARE @tblChildren TABLE ( rownum int identity(1,1) NOT NULL, ParentId int, ChildId int ) INSERT @tblChildren SELECT ParentId, ChildId FROM MainTable WHERE ParentId = @ParentId DECLARE @rownum int SET @rownum = 1 DECLARE @MaxRec int SET @MaxRec = (SELECT count(*) FROM @tblChildren) DECLARE @ChildId int WHILE @rownum <= @MaxRec BEGIN SET @ChildId = (SELECT ChildId FROM @tblChildren WHERE rownum = @rownum) INSERT @tblTree SELECT ParentId, ChildId FROM @tblChildren WHERE rownum = @rownum UNION SELECT ParentId, ChildId FROM f_GetChildren(@ChildId) SET @rownum = @rownum + 1 END RETURN END |
|
RyanRandall
Master Smack Fu Yak Hacker
1074 Posts |
Posted - 2008-04-16 : 10:01:11
|
Looks like an interesting (and fun) problem. Could you explain it a bit more? Is this literally parents and children, or just metaphorically? Can children have > 1 parent? Can children have > 2 parents?Some sample data and an example would be great. Ryan Randall Solutions are easy. Understanding the problem, now, that's the hard part. |
 |
|
Lumbago
Norsk Yak Master
3271 Posts |
|
lianaent
Starting Member
5 Posts |
Posted - 2008-04-16 : 11:06:56
|
Thanks Ryan,Actually, it was fun!Sorry for the lack of example, but here's a simple one. Say you have a table like (lousy naming convention I know, but I haven't had my morning coffee yet, and you get the idea):ChildId ChildName ParentId1 P1 02 P2 03 P1C1 14 P1C2 15 P2C1 26 P1C2C1 47 P1C1C1 38 P1C1C1C1 79 P2C1C1 510 P1C3 1Let's say you want to return the entire P1 family tree. P1's ChildId is 1, so look for any ParentId's of 1 - P1C1 (3), P1C2 (4), P1C3 (10). P1C1 is in turn a child of P1C1C1 (7) which is a child of P1C1C1C1 (8). And so on.When you run the above function:select * from f_getchildren(1)You get:ParentId ChildId1 33 77 81 44 61 10Of course you can start anywhere in the tree:select * from f_getchildren(3)ParentId ChildId3 77 8I went through a ton of very complex code samples, some using .Net, some saying it couldn't be done. But the answer is so simple I felt I could save others a lot of digging.Of course you can then use this function from a stored procedure to return the ChildName and any other info and return it to your front end. For example, the following procedure returns all the data in the table for the Parent P1 in generational order (first generation hits first, then all second generation hits, etc.)ALTER PROCEDURE dbo.GetChildren @ParentId intASSET NOCOUNT ONSELECT * FROM MainTable WHERE ChildId IN ( SELECT ParentId FROM f_GetChildren(@ParentId) UNION SELECT ChildId FROM f_GetChildren(@ParentId) )ORDER BY ParentIdSET NOCOUNT OFFRETURNreturns:ChildId ChildName ParentId1 P1 03 P1C1 14 P1C2 110 P1C3 17 P1C1C1 36 P1C2C1 48 P1C1C1C1 7Hope that clarifies it!Larry |
 |
|
lianaent
Starting Member
5 Posts |
Posted - 2008-04-16 : 11:17:37
|
Thanks Lumbago,I didn't look at all those solutions in detail, but like I said, they all look more complex or less intuitive, and some don't seem to return tables that I can see. Larry |
 |
|
RyanRandall
Master Smack Fu Yak Hacker
1074 Posts |
Posted - 2008-04-16 : 11:40:53
|
So there's only one parent, per child? In that case, I find it easiest to understand and work with if you create a function which returns the 'path' of ancestors.To illustrate it with your example, it's something like this. Note the AncestorPath column in the results at the end.-- Structurecreate table dbo.MyPeople (Id int, Name varchar(30), ParentId int)-- Datainsert dbo.MyPeople select 1, 'P1', nullunion all select 2, 'P2', nullunion all select 3, 'P1C1', 1union all select 4, 'P1C2', 1union all select 5, 'P2C1', 2union all select 6, 'P1C2C1', 4union all select 7, 'P1C1C1', 3union all select 8, 'P1C1C1C1', 7union all select 9, 'P2C1C1', 5union all select 10, 'P1C3', 1go-- Functioncreate function dbo.AncestorPath(@Id int) returns varchar(100) asbegin declare @Path varchar(100) while 0 = 0 begin select @Path = cast(Id as varchar(5)) + isnull('/' + @Path, ''), @Id = ParentId from dbo.MyPeople where Id = @Id if @@rowcount = 0 break end return @Pathendgo-- Calculationselect * from ( select *, dbo.AncestorPath(Id) as AncestorPath from dbo.MyPeople) awhere '/' + AncestorPath + '/' like '%/1/%'/* ResultsId Name ParentId AncestorPath----------- ------------------------------ ----------- -------------1 P1 NULL 13 P1C1 1 1/34 P1C2 1 1/46 P1C2C1 4 1/4/67 P1C1C1 3 1/3/78 P1C1C1C1 7 1/3/7/810 P1C3 1 1/10*/ Ryan Randall Solutions are easy. Understanding the problem, now, that's the hard part. |
 |
|
|
|
|
|
|