views:

34

answers:

1

I have a simple self referencing table as depicted here:

CREATE TABLE [dbo].[Project](
    [ProjectId] [int] NOT NULL,
    [ProjectName] [nvarchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [ParentProjectId] [int] NULL
 CONSTRAINT [PK_Project] PRIMARY KEY CLUSTERED 
(
    [ProjectId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[Project]  WITH CHECK
ADD  CONSTRAINT [FK_Project_Project] FOREIGN KEY([ParentProjectId])
REFERENCES [dbo].[Project] ([ProjectId])

I am trying to get a stored proc that will output the records in a tree view type way with a generated field that will index the level of each of the items like a table of contents like so:

1.0       parent
1.1       1st child
1.2       2nd child
1.2.1    1st grandchild of 2nd child

i have this proc that returns lvl but that's a flat representation and i want a field such as the numbers displayed above (1.1.1, 1.2 and so on)

CREATE PROCEDURE [dbo].[rpt_ExpandProjectList_stefano]    
( @ProjectId int = null)    
AS    
BEGIN    
WITH ProjectList(ProjectId, ParentProjectId, ProjectName,
                 ParentPath, Fullpath, Level)  
AS  
(  
-- Anchor member definition  
    SELECT p.ProjectId, p.ParentProjectId, p.ProjectName, CONVERT(nvarchar(1000), ''), CONVERT(nvarchar(1000), p.ProjectName) AS FullPath, 0 AS Level  
    FROM Project AS p     
    WHERE p.ProjectId = @ProjectId  
    UNION ALL  
-- Recursive member definition  
    SELECT p.ProjectId, p.ParentProjectId, p.ProjectName, CONVERT(nvarchar(1000), d.FullPath), CONVERT(nvarchar(1000), d.FullPath + '|' + p.ProjectName) as FullPath, Level + 1  
    FROM Project AS p  
    INNER JOIN ProjectList AS d  
       ON p.ParentProjectId = d.ProjectId  
)  
-- Statement that executes the CTE  
SELECT pl.ProjectId, pl.ParentProjectId, pl.ProjectName, pl.ParentPath, pl.FullPath, pl.Level  
FROM ProjectList pl  
ORDER BY pl.FullPath  
END
A: 

Here's a version that may help

CREATE PROCEDURE [dbo].[rpt_ExpandProjectList_stefano]    
( @ProjectId int = null)    
AS    
BEGIN    

WITH ProjectNode (ProjectId, ParentProjectId, ProjectName, ProjectNode)
AS
(
-- Derive project node ID
    SELECT ProjectId, ParentProjectId, ProjectName,
        CAST(ROW_NUMBER() OVER (PARTITION BY ParentProjectId ORDER BY ProjectId) AS VARCHAR) AS ProjectNode
    FROM Project
),

    ProjectList(ProjectId, ParentProjectId, ProjectName,
                 ParentPath, Fullpath, Level, FullNodePath)  
AS  
(
-- Anchor member definition  
    SELECT p.ProjectId, p.ParentProjectId, p.ProjectName, CONVERT(nvarchar(1000), ''), 
        CONVERT(nvarchar(1000), p.ProjectName) AS FullPath, 0 AS Level,
        CONVERT(nvarchar(1000), p.ProjectNode) AS FullNodePath
    FROM ProjectNode AS p     
    WHERE p.ProjectId = @ProjectId  
    UNION ALL  
-- Recursive member definition  
    SELECT p.ProjectId, p.ParentProjectId, p.ProjectName, CONVERT(nvarchar(1000), d.FullPath), 
        CONVERT(nvarchar(1000), d.FullPath + '|' + p.ProjectName) as FullPath, Level + 1,
        CONVERT(nvarchar(1000), d.FullNodePath + '.' + p.ProjectNode) AS FullNodePath       
    FROM ProjectNode AS p  
    INNER JOIN ProjectList AS d  
       ON p.ParentProjectId = d.ProjectId  
)

-- Statement that executes the CTE  
SELECT pl.ProjectId, pl.ParentProjectId, pl.ProjectName, pl.ParentPath, pl.FullPath, pl.Level, pl.FullNodePath
FROM ProjectList pl  
ORDER BY pl.FullPath  
END

You'll find an additional CTE (before your original CTE) that derives a row number value for each group of ParentProjectId values. The row number is used as the numeric ID element.

bobs
@bobs as this table has a FK constraint on projectID and ParentProjectID when we run the SP, it is giving "The maximum recursion 100 has been exhausted" error!
Aamod Thakur
@Aamod Thakur - What is the maximum level value in your data? Are there many children, grandchildren, etc. projects for each top-level project. Does the original query produce the same error?
bobs
Yes, in my case i have taken projectID=1 and ParentProjectID =1 for the root node.
Aamod Thakur
I think what you get with a recursive CTE and your values is an infinite loop. The max recursion 100 setting kills the statement instead of staying in the loop. For your top-level projects, what are the ParentProjectId values (NULL, indicating "no Parent project", would be the desirable value)?
bobs