3

我有这张桌子:

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 已关闭以进行重建),但它肯定是正确的?

4

3 回答 3

5

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

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

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

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

于 2012-09-23T08:59:56.300 回答
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 中引用它的所有地方定义该派生表。

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

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

于 2012-09-23T05:50:37.997 回答
0

两者都不正确

在第一次缺少
c2.CatId = @categoryId

在第二个
WHERE CatId = @tParentId

于 2012-09-23T12:53:32.067 回答