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 |
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-14 : 15:51:43
|
| Hi all!I'm playing around with a genealogy database.I've got this table:PERSON----------------Id Name ParentId0 A NULL1 B 02 C 13 D NULL4 E 35 F 46 G 5And now I'de like to find out if there is a relationship between two given PERSONs. For example, if I'd ask the database if there is a relationship between A and C, the answer would be true. However, if I'd ask if G is related to A, the answer would be false.Maybe the database design is wrong? Is there another way of doing this, a best-practice when dealing with parent-child-relationships?I've spent all day trying to solve this... so I would be very greatful for assistance.Thanks! |
|
|
hanbingl
Aged Yak Warrior
652 Posts |
Posted - 2010-04-14 : 16:10:36
|
[code];WITH retable (ID, name, name2, parentid)AS ( SELECT root.ID, root.NAME, '', root.PARENTID FROM PERSON root WHERE root.parentid is null UNION ALL SELECT t.ID, t.NAME, r.NAME, t.PARENTID FROM PERSON t , retable r where t.ParentID = r.ID)SELECT * FROM retable order by ID[/code]quote: Originally posted by Albertinho Hi all!I'm playing around with a genealogy database.I've got this table:PERSON----------------Id Name ParentId0 A NULL1 B 02 C 13 D NULL4 E 35 F 46 G 5And now I'de like to find out if there is a relationship between two given PERSONs. For example, if I'd ask the database if there is a relationship between A and C, the answer would be true. However, if I'd ask if G is related to A, the answer would be false.Maybe the database design is wrong? Is there another way of doing this, a best-practice when dealing with parent-child-relationships?I've spent all day trying to solve this... so I would be very greatful for assistance.Thanks!
|
 |
|
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-15 : 03:42:50
|
Thanks, hanbingl! But now I get the following error:Msg 240, Level 16, State 1, Line 2Types don't match between the anchor and the recursive part in column "name2" of recursive query "retable". I don't understand, which column is being matched with name2? |
 |
|
|
Sachin.Nand
2937 Posts |
Posted - 2010-04-15 : 05:38:06
|
Try this;WITH retable (ID, name, name2, parentid)AS ( SELECT root.ID, root.NAME, convert(varchar(the data length of name column),''), root.PARENTID FROM PERSON root WHERE root.parentid is null UNION ALL SELECT t.ID, t.NAME, r.NAME, t.PARENTID FROM PERSON t , retable r where t.ParentID = r.ID)SELECT * FROM retable order by ID PBUH |
 |
|
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-15 : 06:12:11
|
| Yes, it works! Many thanks! Pax Vobiscum |
 |
|
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-15 : 06:26:31
|
Oops, I was a bit too fast. I added one row: where name='C' and name2='A' Like this:;WITH retable (ID, name, name2, parentid)AS ( SELECT root.ID, root.NAME, convert(nvarchar(50),''), root.PARENTID FROM PERSON root WHERE root.parentid is null UNION ALL SELECT t.ID, t.NAME, r.NAME, t.PARENTID FROM PERSON t , retable r where t.ParentID = r.ID)SELECT * FROM retable WHERE name='C' AND name2='A'order by ID ...but it returns zero rows? Which means, the query works fine to check if B is related to A, but not to check if C is related to A. |
 |
|
|
Sachin.Nand
2937 Posts |
Posted - 2010-04-15 : 07:37:31
|
You cannot directly get the relationship between C & A not that I know of.Try thisdeclare @Person as table(Id varchar(30), Name varchar(60), ParentId varchar(30))insert into @Personselect '0', 'A', NULL union allselect '1', 'B', 0 union allselect '2', 'C', '1' union allselect '3', 'D', NULL union allselect '4', 'E', '3' union allselect '5', 'F', '4';with cteas(select Name,Id,ParentId,convert(varchar(100),Name)as relationship from @Person Funion allselect Pe.Name,Pe.Id,Ct.ParentId,convert(varchar(100),Ct.relationship + '>' + Pe.Name ) from @Person Pe inner join cte Cton Ct.Id=Pe.ParentId)select Name,relationship from cte Cwhere C.ParentId IS NULL --option (maxrecursion 0) PBUH |
 |
|
|
mandm
Posting Yak Master
120 Posts |
Posted - 2010-04-15 : 14:04:07
|
| Try this. The HierarchyPath is just a running list of ID's from the parent through each level child in binary form. This is useful for making sure sorting is handled properly when you want to see the parent id's immediately followed by the children. You can also search the path for specific parents or children. I'm sure there are probably other ways to do this. This is just one I find useful.;WITH retable (ID, name, name2, parentid, HierarchyPath)AS ( SELECT root.ID , root.NAME , convert(nvarchar(50),'') , root.PARENTID , CONVERT(VARBINARY(900) , CAST(root.ID AS BINARY(4))) AS HierarchyPath FROM PERSON root WHERE root.parentid is null UNION ALL SELECT t.ID , t.NAME , r.NAME , t.PARENTID , CONVERT(VARBINARY(900),r.HierarchyPath + CAST(t.ID AS BINARY(4))) AS HierarchyPath FROM PERSON t , retable r where t.ParentID = r.ID)SELECT * FROM retable WHERE CHARINDEX(CAST(0 AS BINARY(4)), [HierarchyPath]) > 0 AND CHARINDEX(CAST(2 AS BINARY(4)), [HierarchyPath]) > 0 ORDER BY [HierarchyPath]Just noticed that since you are using name you would need to arrive at the ID values first then do the search. I have edited the where clause to use the id's for now. |
 |
|
|
hanbingl
Aged Yak Warrior
652 Posts |
Posted - 2010-04-15 : 17:46:39
|
If all you need is to display True/False use case when in the select:select case when name2 = 'A' then 'True' else 'False'from retable where name = 'C' quote: Originally posted by Albertinho Oops, I was a bit too fast. I added one row: where name='C' and name2='A' Like this:;WITH retable (ID, name, name2, parentid)AS ( SELECT root.ID, root.NAME, convert(nvarchar(50),''), root.PARENTID FROM PERSON root WHERE root.parentid is null UNION ALL SELECT t.ID, t.NAME, r.NAME, t.PARENTID FROM PERSON t , retable r where t.ParentID = r.ID)SELECT * FROM retable WHERE name='C' AND name2='A'order by ID ...but it returns zero rows? Which means, the query works fine to check if B is related to A, but not to check if C is related to A.
|
 |
|
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-16 : 05:21:10
|
mandm: This seems to work! But I'm not sure I really understood this? When asking for relationship between A and C, the result is one row (C), when asking for relationship between A and B the result is two rows (B and C). Which is fine, because I can see that there is a relationship between the two. And when asking about A and F, the result is zero rows, correctly. So if I use this I will simply count number of rows, right?Many thanks again!! And, hanbingl, thanks for the tip about case! |
 |
|
|
Albertinho
Starting Member
6 Posts |
Posted - 2010-04-16 : 06:21:08
|
Ok guys, below is the answer:declare @up1 varchar(1)declare @up2 varchar(1)set @up1 = 'D'set @up2 = 'F';with cteas(select Name, Id, ParentId, Name as relationship from Person Fwhere Name=@up1union allselect Pe.Name, Pe.Id, Ct.ParentId, Ct.relationship from Person Pe inner join cte Cton Ct.Id=Pe.ParentId)select * from cte Cwhere Name=@up2 order by C.Id I can ask if @up1 is parent of @up2, no matter how many generations between the two. So hanbingl gave me the correct answer, I just didn't get it at first! I guess I realy wasn't sure what I was looking for either. But I am now. Cheers! |
 |
|
|
|
|
|
|
|