Как ограничить глубину рекурсии CTE, но выбрать общую таблицу? - PullRequest
5 голосов
/ 16 января 2012

В настоящее время у нас есть хранимая процедура, которая возвращает данные из таблицы в ее исходной схеме, выполняя что-то вроде этого:

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

Это хорошо, потому что независимо от того, как меняется схема таблицы, до тех пор, покаесть столбцы [Id] и [ParentId], мне не нужно обновлять эту хранимую процедуру.Я хотел бы сделать нечто подобное, но также иметь возможность динамически определять глубину рекурсии.Единственный способ сделать это - добавить идентификатор уровня / глубины, например:

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

. Это хорошо работает, но убирает главный плюс предыдущего запроса, так как при выборе * вконец также даст мне уровень.Есть ли какой-то другой способ сделать это, где я мог бы указать уровень, а также выбрать все столбцы из таблицы?Заранее спасибо.

1 Ответ

13 голосов
/ 16 января 2012

Если все, что вы хотите сделать с полем уровня, это ограничить количество рекурсий, вы можете использовать MAXRECURSION подсказку запроса , что-то вроде этого:

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)

Редактировать:

Отвечая на вопрос в комментариях, нет, вы не можете подавить ошибку, которая появляется при повторении больше раз, чем позволяет ваш параметр MAXRECURSION.Если я вас правильно понимаю, вы могли бы сделать что-то вроде этого:

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;

Это должно быть в той же степени, что и у вас выше, при условии, что вы не планируете изменять поля иерархического или первичного ключа.

...