Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Limit CTE Recursion Depth but Select Generic Table?

Currently we have a stored procedure that returns data from a table in it's original schema by doing something like this:

WITH CTE AS
(
    -- Start CTE off by selecting the id that was provided to stored procedure.
    SELECT *
    FROM [dbo].[TestTable]
    WHERE [Id] = 1
    -- Recursively add tasks that are children of records already found in previous iterations.
    UNION ALL
    SELECT t.*
    FROM [dbo].[TestTable] as t
    INNER JOIN CTE as tcte
        ON t.[ParentId] = tcte.[Id]
)           
SELECT *
FROM CTE

This is nice, because no matter how the table schema changes, as long as there are [Id] and [ParentId] columns, I won't have to update this stored procedure. I'd like to do something similar, but also be able to specify the depth of the recursion dynamically. The only way I've seen to do this is to add a Level/Depth identifier like so:

WITH CTE AS
(
    -- Start CTE off by selecting the task that was provided to stored procedure.
    SELECT *, 0 as [Level]
    FROM [dbo].[TestTable]
    WHERE [Id] = 1
    -- Recursively add tasks that are children of parent tasks that have already been found in previous iterations.
    UNION ALL
    SELECT t.*, [Level] + 1
    FROM [dbo].[TestTable] as t
    INNER JOIN CTE as tcte
        ON t.[ParentId] = tcte.[Id]
    WHERE [Level] < 2
)           
SELECT *
FROM CTE

This works well, but takes away the major plus of the previous query since selecting * at the end will give me the level as well. Is there some other way of doing this where I could specify a level, but also generically select all columns from the table? Thanks in advance.

like image 989
Ocelot20 Avatar asked Jan 16 '12 19:01

Ocelot20


People also ask

How do I limit recursion on CTE?

We can change the CTEs default maximum recursion by specifying the MAXRECURSION query hint. Change the previous recursive CTE to generate numbers between 1 to 200 by specifying the MAXRECURSION hint value as 210 as below and verify the result: ?

Which option is used to restrict the recursion to a certain value?

You can define the maximum number of recursions for CTE, using the MAXRECURSION option. Set the value of MAXRECURSION to 0, if you don't know the exact numbers of recursions.

What is considered to be a disadvantage of using recursive common table expression?

Disadvantages of CTEThe CTE can only be referenced once by the Recursive member. We cannot use the table variables and CTEs as parameters in stored procedures. We already know that the CTE could be used in place of a view, but a CTE cannot be nested, while Views can.

Which is a common scenario in which you would use a recursive common table expression CTE )?

The recursive CTE is useful when working with hierarchical data as the CTE continues to execute until the query returns the entire hierarchy. A typical example of hierarchical data is a table that includes a list of employees. For every employee, the table provides a reference to that person's manager.

Can we use multiple CTE in stored procedure?

After learning common table expressions or CTEs, a natural question is “Can I use several CTEs in one query?” Yes, you can!


1 Answers

If all you want to do with your level field is limit the number of recursions, you should be able to use a MAXRECURSION query hint, something like this:

WITH Department_CTE AS
(
    SELECT
        DepartmentGroupKey,
        ParentDepartmentGroupKey,
        DepartmentGroupName
    FROM dimDepartmentGroup
    WHERE DepartmentGroupKey = 2
    UNION ALL
    SELECT
        Child.DepartmentGroupKey,
        Child.ParentDepartmentGroupKey,
        Child.DepartmentGroupName
    FROM Department_CTE AS Parent
        JOIN DimDepartmentGroup AS Child
            ON Parent.ParentDepartmentGroupKey = Child.DepartmentGroupKey
)
SELECT * FROM Department_CTE
OPTION (MAXRECURSION 2)

Edit:

In answer to the question in the comments, no, you can't suppress the error that you get when recursing more times than your MAXRECURSION setting allows. If I understand you correctly, you could do something like this:

WITH CTE AS
(
    -- Start CTE off by selecting the task that was provided to stored procedure.
    SELECT Id, 0 as [Level]
    FROM [dbo].[TestTable]
    WHERE [Id] = 1
    -- Recursively add tasks that are children of parent tasks that have already been found in previous iterations.
    UNION ALL
    SELECT t.Id, [Level] + 1
    FROM [dbo].[TestTable] as t
    INNER JOIN CTE as tcte
        ON t.[ParentId] = tcte.[Id]
    WHERE [Level] < 2
),
CTE2 AS
(
    SELECT TestTable.*
    FROM CTE
        INNER JOIN TestTable ON CTE.Id = TestTable.Id
)
SELECT * FROM CTE2;

This should be equally as generic as what you have above, assuming you're not planning on changing the hierarchical or primary key fields.

like image 101
mwigdahl Avatar answered Sep 28 '22 06:09

mwigdahl