sql – 为什么递归CTE在程序上运行分析函数(ROW_NUMBER)?

前端之家收集整理的这篇文章主要介绍了sql – 为什么递归CTE在程序上运行分析函数(ROW_NUMBER)?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我昨天回答一个递归的CTE,暴露了一个问题,这些在sql Server中实现的方式(也可能在其他RDBMS中).基本上,当我尝试对当前递归级别使用ROW_NUMBER时,它会针对当前递归级别的每一行子集运行.我会期望这将在真正的SET逻​​辑中工作,并且针对整个当前递归级别运行.

看来,from this MSDN article,我发现的问题是功能

Analytic and aggregate functions in the recursive part of the CTE are
applied to the set for the current recursion level and not to the set
for the CTE. Functions like ROW_NUMBER operate only on the subset of
data passed to them by the current recursion level and not the entire
set of data pased to the recursive part of the CTE
. For more
information,see J. Using analytical functions in a recursive CTE.

在我的挖掘中,我无法找到解释为什么选择这样做是为了工作呢?这是一个基于集合的语言的程序化方法,所以这对我的sql思维过程起反作用,在我看来相当混乱.有没有人知道和/或任何人可以解释为什么递归CTE以递归方式在递归级别处理分析函数

以下是帮助可视化的代码

注意,每个代码输出中的RowNumber列.

Here is the SQLFiddle for the CTE (only showing the 2nd level of the recursion)

  1. WITH myCTE
  2. AS
  3. (
  4. SELECT *,ROW_NUMBER() OVER (ORDER BY score desc) AS RowNumber,1 AS RecurseLevel
  5. FROM tblGroups
  6. WHERE ParentId IS NULL
  7.  
  8. UNION ALL
  9.  
  10. SELECT tblGroups.*,ROW_NUMBER() OVER (ORDER BY myCTE.RowNumber,tblGroups.score desc) AS RowNumber,RecurseLevel + 1 AS RecurseLevel
  11. FROM tblGroups
  12. JOIN myCTE
  13. ON myCTE.GroupID = tblGroups.ParentID
  14. )
  15. SELECT *
  16. FROM myCTE
  17. WHERE RecurseLevel = 2;

Here is the second SQLFiddle for what I would expect the CTE to do (again only need the 2nd level to display the issue)

  1. WITH myCTE
  2. AS
  3. (
  4. SELECT *,1 AS RecurseLevel
  5. FROM tblGroups
  6. WHERE ParentId IS NULL
  7. )
  8. SELECT tblGroups.*,RecurseLevel + 1 AS RecurseLevel
  9. FROM tblGroups
  10. JOIN myCTE
  11. ON myCTE.GroupID = tblGroups.ParentID;

我总是设想sql递归CTE运行更像this while loop

  1. DECLARE @RecursionLevel INT
  2. SET @RecursionLevel = 0
  3. SELECT *,@RecursionLevel AS recurseLevel
  4. INTO #RecursiveTable
  5. FROM tblGroups
  6. WHERE ParentId IS NULL
  7.  
  8. WHILE EXISTS( SELECT tblGroups.* FROM tblGroups JOIN #RecursiveTable ON #RecursiveTable.GroupID = tblGroups.ParentID WHERE recurseLevel = @RecursionLevel)
  9. BEGIN
  10.  
  11. INSERT INTO #RecursiveTable
  12. SELECT tblGroups.*,ROW_NUMBER() OVER (ORDER BY #RecursiveTable.RowNumber,recurseLevel + 1 AS recurseLevel
  13. FROM tblGroups
  14. JOIN #RecursiveTable
  15. ON #RecursiveTable.GroupID = tblGroups.ParentID
  16. WHERE recurseLevel = @RecursionLevel
  17. SET @RecursionLevel = @RecursionLevel + 1
  18. END
  19.  
  20. SELECT * FROM #RecursiveTable ORDER BY RecurseLevel;

解决方法

分析功能是特殊的,它们需要一个已知的结果集来解决.
它们依赖于以下,前面的或完整的结果集来计算当前值.
也就是说,合并视图从不允许包含分析函数的视图.为什么?
这样会改变结果.

例如:

  1. Select * from (
  2. select row_number() over (partition by c1 order by c2) rw,c3 from t) z
  3. where c3=123

不一样

  1. select row_number() over (partition by c1 order by c2) rw,c3 from t
  2. where c3=123

这两个将为rw返回不同的值.
这就是为什么包含分析函数的子查询将始终在其中完全解决,而不会与其余的合并.

更新

看第二个查询

  1. WITH myCTE
  2. AS
  3. (
  4. SELECT *,RecurseLevel + 1 AS RecurseLevel
  5. FROM tblGroups
  6. JOIN myCTE
  7. ON myCTE.GroupID = tblGroups.ParentID;

它的工作原理就好像是(相同的执行计划和结果):

  1. SELECT tblGroups.*,RecurseLevel + 1 AS RecurseLevel
  2. FROM tblGroups
  3. JOIN (
  4. SELECT *,1 AS RecurseLevel
  5. FROM tblGroups
  6. WHERE ParentId IS NULL
  7. )myCTE ON myCTE.GroupID = tblGroups.ParentID;

这个需要被分区以重置rownumber.

递归查询在while循环中不起作用,它们不是程序性的.在基础上,它们像递归函数一样工作,但是根据表,查询,索引,它们可以被优化为以一种方式运行.

如果我们遵循在使用分析函数时查看无法合并的概念,并且查看查询1.它只能运行一次,并且在嵌套循环中.

  1. WITH myCTE
  2. AS
  3. ( /*Cannot be merged*/
  4. SELECT *,1 AS RecurseLevel,cast(0 as bigint) n
  5. FROM tblGroups
  6. WHERE ParentId IS NULL
  7.  
  8. UNION ALL
  9.  
  10. /*Cannot be merged*/
  11. SELECT tblGroups.*,RecurseLevel + 1 AS RecurseLevel,myCTE.RowNumber
  12. FROM tblGroups
  13. JOIN myCTE
  14. ON myCTE.GroupID = tblGroups.ParentID
  15. )
  16. SELECT *
  17. FROM myCTE;

所以第一选择,不能合并第二,也不.运行此查询的唯一方法是在每个级别返回的每个项目的嵌套循环中,因此重置.再次,这不是程序性的问题,而是一个可能的执行计划的问题.

希望这回答你的问题,让我如果不:)

ÿ

猜你在找的MsSQL相关文章