【问题标题】:Recursion in SQL - Common Table Expression vs WHILESQL 中的递归 - 公用表表达式与 WHILE
【发布时间】:2012-09-15 00:34:20
【问题描述】:

我有这张桌子:

Categories( CatId, Name, ParentId NULL )

...这是递归的,因此每个类别都可以命名一个父类别,该父类别又具有一个父类别,依此类推。像这样:

1, "Sports", NULL
2, "Football", 1
3, "Golf", 1
4, "Handegg", 2
5, "Sex", NULL
6, "On the beach", 5

不久前,我使用 CTE 对类别的父级执行递归查找,如下所示:

WITH Categories(CatId, Name, ParentId, n) AS (
    SELECT CatId, Name, ParentId, 1
    FROM Categories
    WHERE CatId = @categoryId

    UNION ALL

    SELECT c1.CatId, c1.Name, c1.ParentId, c2.n + 1
    FROM Categories AS c1
         INNER JOIN Categories AS c2 ON c1.CatId = c2.ParentId
)

但我在想,我不能将其改写为 WHILE 查询吗?

DECLARE @ret TABLE( CatId, Name, ParentId )

DECLARE @tCatId int
DECLARE @tName nvarchar(255)
DECLARE @tParentId int NULL

SELECT @tCatId = CatId, @tName = Name, @tParentId = ParentId
FROM Categories
WHERE CatId = @categoryId

WHILE( @tParentId IS NOT NULL ) BEGIN
    INSERT INTO @ret ( CatId, Name, ParentId ) VALUES ( @tCatId, @tName, @tParentId )

    SELECT @tCatId = CatId, @tName = Name, @tParentId = ParentId
    FROM Categories
    WHERE CatId = @tParentId
END

SELECT @ret

显然,它的边缘有点粗糙(例如当第一行的 ParentId 为 NULL 时未完成),而且我无法对其进行测试(因为我的 SQL Server 已关闭以进行重建),但肯定是一样正确吗?

【问题讨论】:

标签: sql tsql recursion common-table-expression


【解决方案1】:

CTE 用于表示一个临时结果集,您可以使用它来编写复杂的查询。

WITH DerviedTable (<Column>) AS (SELECT query to derive a table from one or more tables)
<A complex SELECT query that uses the DerviedTable one or more times>

在没有 CTE 的情况下,您必须在需要在 JOINS 中引用它的所有地方定义该派生表。

使用UNION ALL 进行递归的CTE 当然补充了基于WHILE 的递归逻辑,但正如您所见,它消除了编写TSQL 的麻烦,只需定义基和递归成员。

在我们之前的一个 MSSQL Server 项目中,当我们迁移到 SQL Server 2005 时,我们积极地将所有此类基于 WHILE 的递归替换为 CTE,并遇到了table variables involved in CTE definition 的死锁情况。不确定这是否已在最近的版本中得到修复,但认为值得分享。

【讨论】:

    【解决方案2】:

    虽然这在语义上可行,但 CTE 通常更易于编程,因为它们代表不可变的逻辑结果集。 while 循环命令式地指示执行模型。出于这个原因,正确和维护需要更多的工作。

    CTE 很有可能会更快。在内部,它包含一种形式的 while 循环,但该循环位于查询执行管道的深处。它比 T-SQL 循环快得多。此外,它是一个单一的声明。 while 循环运行许多语句。 SQL Server 的每条语句开销很小(即使它只是 select null)。

    也就是说,while 循环有时会为您提供更多的灵活性和更多的控制权。

    您可能应该首先尝试通过 CTE 解决您的要求。仅当您注意到(或您可以预见)由于某些具体原因,while 循环将成为更好的解决方案时,才使用命令式控制流。

    【讨论】:

      【解决方案3】:

      都不对

      第一次失踪
      c2.CatId = @categoryId

      第二个
      其中 CatId = @tParentId

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-05
        • 1970-01-01
        • 1970-01-01
        • 2019-11-04
        • 1970-01-01
        • 2015-03-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多