【发布时间】:2012-04-16 08:05:28
【问题描述】:
我昨天回答了一个递归 CTE,它暴露了这些在 SQL Server 中的实现方式问题(可能也在其他 RDBMS 中?)。基本上,当我尝试针对当前递归级别使用ROW_NUMBER 时,它会针对每一行当前递归级别的子集 运行。我希望这将在真正的 SET 逻辑中工作,并针对整个当前递归级别运行。
看来,from this MSDN article,我发现的问题是预期功能:
CTE 递归部分中的分析和聚合函数是 应用于当前递归级别的集合而不是集合 对于 CTE。 像 ROW_NUMBER 这样的函数只对 通过当前递归级别而不是全部传递给它们的数据 传递给 CTE 递归部分的一组数据。更多 信息,请参阅 J. 在递归 CTE 中使用分析函数。
在我的挖掘中,我找不到任何地方可以解释为什么选择它以这种方式工作?这更像是基于集合的语言中的一种程序方法,因此这与我的 SQL 思维过程相悖,并且在我看来非常令人困惑。 是否有人知道和/或谁能解释为什么递归 CTE 以程序方式在递归级别处理分析函数?
以下是帮助可视化的代码:
注意,每个代码输出中的RowNumber 列。
Here is the SQLFiddle for the CTE (only showing the 2nd level of the recursion)
WITH myCTE
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Score desc) AS RowNumber, 1 AS RecurseLevel
FROM tblGroups
WHERE ParentId IS NULL
UNION ALL
SELECT tblGroups.*,
ROW_NUMBER() OVER (ORDER BY myCTE.RowNumber , tblGroups.Score desc) AS RowNumber,
RecurseLevel + 1 AS RecurseLevel
FROM tblGroups
JOIN myCTE
ON myCTE.GroupID = tblGroups.ParentID
)
SELECT *
FROM myCTE
WHERE RecurseLevel = 2;
WITH myCTE
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Score desc) AS RowNumber, 1 AS RecurseLevel
FROM tblGroups
WHERE ParentId IS NULL
)
SELECT tblGroups.*,
ROW_NUMBER() OVER (ORDER BY myCTE.RowNumber , tblGroups.Score desc) AS RowNumber,
RecurseLevel + 1 AS RecurseLevel
FROM tblGroups
JOIN myCTE
ON myCTE.GroupID = tblGroups.ParentID;
我一直设想 SQL 递归 CTE 运行起来更像 this while loop
DECLARE @RecursionLevel INT
SET @RecursionLevel = 0
SELECT *, ROW_NUMBER() OVER (ORDER BY Score desc) AS RowNumber, @RecursionLevel AS recurseLevel
INTO #RecursiveTable
FROM tblGroups
WHERE ParentId IS NULL
WHILE EXISTS( SELECT tblGroups.* FROM tblGroups JOIN #RecursiveTable ON #RecursiveTable.GroupID = tblGroups.ParentID WHERE recurseLevel = @RecursionLevel)
BEGIN
INSERT INTO #RecursiveTable
SELECT tblGroups.*,
ROW_NUMBER() OVER (ORDER BY #RecursiveTable.RowNumber , tblGroups.Score desc) AS RowNumber,
recurseLevel + 1 AS recurseLevel
FROM tblGroups
JOIN #RecursiveTable
ON #RecursiveTable.GroupID = tblGroups.ParentID
WHERE recurseLevel = @RecursionLevel
SET @RecursionLevel = @RecursionLevel + 1
END
SELECT * FROM #RecursiveTable ORDER BY RecurseLevel;
【问题讨论】:
-
所有递归 CTE 当前都获得相同的基本计划,将行添加到充当堆栈的假脱机,然后使用嵌套循环逐行处理该行。
EXCEPTas per this question 的类似问题 -
@MartinSmith 是的,我明白这一点,但我的问题是为什么它会这样做,因为它可以很容易地处理这是一个基于集合的递归。这是 SQL 的优势,而不是这种过程方法。
-
不知道。猜猜更简单或更有效的实现?大多数暴露逻辑和物理实现之间差异的功能都被禁止。
EXCEPTwill join 那个列表。 Connect Item for ROW_NUMBER 表示他们在 2008 年也曾在这里这样做过,但在某些与hierarchyids 相关的用例中将其颠倒过来 -
@MartinSmith Hrmmm,我与 Paul White 不在同一页面上(因为我仍然不同意),但我确实理解他们的推理来自哪里。如果您发表您的评论作为答案,我会接受,因为这就是我正在寻找的...MS 推理
标签: sql sql-server common-table-expression row-number recursive-cte