sql - 如何限制CTE递归深度但选择通用表?

标签 sql sql-server sql-server-2005 common-table-expression

目前,我们有一个存储过程,它通过执行以下操作从原始架构中的表返回数据:

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

这效果很好,但带走了上一个查询的主要优点,因为最后选择 * 也会给我级别。是否有其他方法可以指定级别,但通常也可以从表中选择所有列?提前致谢。

最佳答案

如果您只想限制递归次数,则您应该能够使用 MAXRECURSION query hint ,像这样:

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;

假设您不打算更改层次结构或主键字段,这应该与上面的内容一样通用。

关于sql - 如何限制CTE递归深度但选择通用表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8885170/

相关文章:

php - 在多层成员数据库中确定 "Number of Users below"

Mysql,从另一个表的每条记录插入表

sql - 嵌套选择中的分区返回奇怪的结果

sql-server - 查询基准测试

sql-server - SQL Server : COLLATE CS->CI performance vs UPPER

SQL查询根据时间范围计算金额

sql-server - 保持SQL Server数据库和Elasticsearch索引同步

sql - 有没有更好的方法从 hh :mm:ss to hhmmss? 转换 SQL 日期时间

sql - udf 与直接 sql 性能

sql - 如何编写 SQL Server 登录密码更改脚本?