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 |
|
edsilv
Starting Member
9 Posts |
Posted - 2007-06-28 : 15:30:01
|
| Hi,I'm building an application where items are part of hierarchical "chain" consisting of chain segments.Here are the relevant tables.Chains:Id (int)UserId (Guid)ChainSegments:Id (int)ChainId (int - foreign key to Chains table)ParentId (int - foreign key to Id column in same table)UserId (Guid)There are more columns in ChainSegments but they are not relevant in this context.You can see here that ChainSegments belong to Chains and ChainSegments can have other ChainSegments as children.I'm trying to write a recursive Sql query that finds a ChainSegment for a given ChainId and recursively selects its "child" ChainSegments for a given number of iterations. Hope this is making sense so far :-)Here's what I've got:GOWITH ChainSegmentTree (Id, ParentId, Level)AS(-- Anchor member definition SELECT a.Id, a.ParentId, 0 AS Level FROM ChainSegments AS a INNER JOIN ChainSegments AS b ON a.Id = b.ParentId WHERE a.ParentId IS NULL UNION ALL-- Recursive member definition SELECT a.Id, a.ParentId, Level + 1 FROM ChainSegments AS a INNER JOIN ChainSegments AS b ON a.Id = b.ParentId INNER JOIN ChainSegmentTree AS c ON a.Id = c.ParentId)-- Statement that executes the CTESELECT e.Id, e.ParentId, LevelFROM ChainSegmentTree AS eINNER JOIN ChainSegments AS f ON e.Id = f.IdWHERE Level = 0;GOI've based this on an example I found here:http://technet.microsoft.com/en-us/library/ms186243.aspxUnfortunately this only returns one row, the "root" ChainSegment (ParentId = null) and nothing else.I'm admittedly rubbish at Sql and would really appreciate any help that could be offered here.Many thanks,Ed |
|
|
Lamprey
Master Smack Fu Yak Hacker
4614 Posts |
Posted - 2007-06-28 : 17:53:12
|
| WHERE Level = 0That is limiting your results to only those that are at level 0. Which, presumably, is only one row..? |
 |
|
|
edsilv
Starting Member
9 Posts |
Posted - 2007-06-28 : 19:08:40
|
| Hi,Thanks for that. That helped but it was still really broken.I think I've fluked a nearly-working query but the problem is it doesn't return rows that don't have children. Anything with a child is included in the result. I need this to include ChainSegments even if they don't have any children.DECLARE @ChainId AS IntSET @ChainId = 1DECLARE @SegmentId AS IntSET @SegmentId = 4;WITH ChainSegmentTree (Id, ChainId, ParentId, Level)AS(-- Anchor member definitionSELECT DISTINCT a.Id, a.ChainId, a.ParentId, 0 AS LevelFROM ck_ChainSegments AS aINNER JOIN ck_ChainSegments AS bON a.Id = b.ParentIdWHERE a.Id = @SegmentId AND a.ChainId = @ChainIdUNION ALL-- Recursive member definitionSELECT a.Id, a.ChainId, a.ParentId, Level + 1FROM ck_ChainSegments AS aINNER JOIN ChainSegmentTree AS bON a.ParentId = b.Id)-- Statement that executes the CTESELECT Id, ChainId, ParentId, LevelFROM ChainSegmentTreeORDER BY Level |
 |
|
|
Lamprey
Master Smack Fu Yak Hacker
4614 Posts |
Posted - 2007-06-28 : 19:59:24
|
I can't test this as I do not have any data, but I think this may work to get you started. Then you might be able to add in the other conditions you are trying to impose. But, let's get the base query qorking first:WITH ChainSegmentTree (Id, ParentId, Level)AS( -- Anchor member definition SELECT a.Id, a.ParentId, 0 AS Level FROM ChainSegments AS a WHERE a.ParentId IS NULL UNION ALL -- Recursive member definition SELECT a.Id, a.ParentId, Level + 1 FROM ChainSegments AS a INNER JOIN ChainSegmentTree AS c ON a.Id = c.ParentId)-- Statement that executes the CTESELECT e.Id, e.ParentId, LevelFROM ChainSegmentTree AS e |
 |
|
|
edsilv
Starting Member
9 Posts |
Posted - 2007-06-28 : 20:10:44
|
| Hi,Yep, I've done the same thing and it now works perfectly:DECLARE @ChainId AS IntSET @ChainId = 1DECLARE @SegmentId AS IntSET @SegmentId = 5DECLARE @Levels AS IntSET @Levels = 5;WITH ChainSegmentCTE (Id, ChainId, ParentId, Level)AS(-- Anchor member definitionSELECT a.Id, a.ChainId, a.ParentId, 0 AS LevelFROM ck_ChainSegments AS aWHERE a.Id = @SegmentId AND a.ChainId = @ChainIdUNION ALL-- Recursive member definitionSELECT a.Id, a.ChainId, a.ParentId, Level + 1FROM ck_ChainSegments AS aINNER JOIN ChainSegmentCTE AS bON a.ParentId = b.Id)-- Statement that executes the CTESELECT Id, ChainId, ParentId, LevelFROM ChainSegmentCTEWHERE Level <= @LevelsORDER BY LevelThanks very much for your help! |
 |
|
|
Lamprey
Master Smack Fu Yak Hacker
4614 Posts |
Posted - 2007-06-29 : 12:53:50
|
| Anytime. Gald you figured it out. :) |
 |
|
|
bwebb
Starting Member
4 Posts |
Posted - 2007-07-20 : 11:14:05
|
| I don't understand the need to set @Levels. Would'nt you want to run until all children have been selected? And if so, does ommitting the final where clause provide this? |
 |
|
|
Lamprey
Master Smack Fu Yak Hacker
4614 Posts |
Posted - 2007-07-20 : 13:29:22
|
| The original requirement was to select only a certian(max) number of levels. So that is why the @Levels is set. If you wanted to return all children for a given set then yes, removing the WHERE clause (Level <= @Levels) would/should return them all. |
 |
|
|
|
|
|
|
|