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 2005 Forums
 Transact-SQL (2005)
 Finding parent-child between certain values?

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 ParentId
0 A NULL
1 B 0
2 C 1
3 D NULL
4 E 3
5 F 4
6 G 5


And 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 ParentId
0 A NULL
1 B 0
2 C 1
3 D NULL
4 E 3
5 F 4
6 G 5


And 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!

Go to Top of Page

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 2
Types 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?
Go to Top of Page

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
Go to Top of Page

Albertinho
Starting Member

6 Posts

Posted - 2010-04-15 : 06:12:11
Yes, it works! Many thanks!

Pax Vobiscum
Go to Top of Page

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.

Go to Top of Page

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 this

declare @Person as table(Id varchar(30), Name varchar(60), ParentId varchar(30))
insert into @Person
select '0', 'A', NULL union all
select '1', 'B', 0 union all
select '2', 'C', '1' union all
select '3', 'D', NULL union all
select '4', 'E', '3' union all
select '5', 'F', '4'


;with cte
as
(
select Name,Id,ParentId,convert(varchar(100),Name)as relationship from @Person F
union all
select Pe.Name,Pe.Id,Ct.ParentId,convert(varchar(100),Ct.relationship + '>' + Pe.Name ) from @Person Pe inner join cte Ct
on Ct.Id=Pe.ParentId

)

select Name,relationship from cte C
where C.ParentId IS NULL



--option (maxrecursion 0)






PBUH
Go to Top of Page

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.
Go to Top of Page

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.



Go to Top of Page

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!
Go to Top of Page

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 cte
as
(
select Name, Id, ParentId, Name as relationship from Person F
where Name=@up1
union all
select Pe.Name, Pe.Id, Ct.ParentId, Ct.relationship from Person Pe
inner join cte Ct
on Ct.Id=Pe.ParentId
)
select * from cte C
where 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!
Go to Top of Page
   

- Advertisement -