我昨天回答了一个递归 CTE,它暴露了在 SQL Server 中实现这些方法的问题(也可能在其他 RDBMS 中?)。基本上,当我尝试ROW_NUMBER
针对当前递归级别使用时,它会针对当前递归级别的每一行子集运行。我希望这将在真正的 SET 逻辑中工作,并针对整个当前递归级别运行。
从这篇 MSDN 文章看来,我发现的问题是预期的功能:
CTE 递归部分中的分析和聚合函数应用于当前递归级别的集合,而不是应用于 CTE 的集合。像 ROW_NUMBER 这样的函数只对当前递归级别传递给它们的数据子集进行操作,而不是传递给 CTE 递归部分的整个数据集。有关更多信息,请参阅 J. 在递归 CTE 中使用分析函数。
在我的挖掘中,我找不到任何地方可以解释为什么选择它以它的方式工作?这更像是基于集合的语言中的一种程序方法,因此这与我的 SQL 思维过程相悖,并且在我看来非常令人困惑。有人知道和/或有人可以解释为什么递归 CTE 以程序方式在递归级别处理分析函数吗?
这是帮助可视化的代码:
请注意,RowNumber
每个代码输出中的列。
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;
这是我期望 CTE 做的第二个 SQLFiddle(再次只需要第二级来显示问题)
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 运行得更像这样 while 循环
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;