为什么递归 CTE 估计只有 1 行?

cro*_*sek 4 sql-server cte sql-server-2012 recursive cardinality-estimates

给定两个级联的、独立的(没有真正的表)递归 CTE:

create view NumberSequence_0_100_View
as
with NumberSequence as
(
    select 0 as Number
    union all
    select Number + 1
      from NumberSequence    
     where Number < 100
)
select Number
  from NumberSequence;
go

create view NumberSequence_0_10000_View
as
select top 10001
       v100.Number * 100 + v1.Number as Number
  from Common.NumberSequence_0_100_View v100
 cross join Common.NumberSequence_0_100_View v1
 where v1.Number < 100
   and v100.Number * 100 + v1.Number <= 10000
    -- please resist complaining about "order by in view" for this question
 order by v100.Number * 100 + v1.Number 
go
Run Code Online (Sandbox Code Playgroud)

然后生成估计/实际计划:

select * from NumberSequence_0_10000_View
Run Code Online (Sandbox Code Playgroud)

估计 在此处输入图片说明 实际的 级联CtePlan

运行时间为 23 毫秒,但仅估计最终输出的一行(仅第一个视图的 2 行)。

问题是,当它被用作连接真实数据的子查询时(例如通过“DaysAgo”),计划通常是一个非常慢的嵌套循环,我经常需要添加连接提示/反向顺序等。

无论如何在保持 CTE 方法的同时改进估计?是否曾经请求过“with (AssumeMinRows=N)”提示?对于许多情况(不仅仅是 CTE),这似乎是一个很好的通用助手。

Pau*_*ite 6

为什么递归 CTE 估计只有 1 行?

递归公用表表达式的基数估计非常有限。

在原始基数估计模型下,估计值是锚点和递归部分的基数估计值的简单总和。这相当于假设递归部分只执行一次。

在 SQL Server 2014 中,启用新的基数估计模型后,逻辑略有更改,以假设递归部分执行 3 次,每次迭代返回的行数相同。

这两个都是未经训练的猜测,因此使用递归 CTes 通常会导致质量较差的估计也就不足为奇了。更一般地说,估计递归过程的结果几乎是不可能的,因此优化器甚至不会尝试。这不会通过使用特别简单的递归结构来改变,显然是为了产生序列号——优化器没有检测这种模式的逻辑。

在您的特定情况下,最终估计是一个,因为优化器进一步猜测谓词的选择性,例如[Recr1007]<(100)(在节点 ID 3 处的过滤器中)和([Recr1003]*(100)+[Recr1007])<=(10000)(节点 ID 2 处的嵌套循环连接上的残差谓词)。同样,这些都是猜测,结果令人遗憾,但并不令人惊讶。

无论如何在保持 CTE 方法的同时改进估计?

不是我所知道的。

是否曾经请求过“with (AssumeMinRows=N)”提示?

不是直接在这些方面。有很多要求实现CTE,如果这种实现带有自动统计数据生成,这将有所帮助。我不会列出其他人,因为您似乎已经对大多数建议发表了评论:)

也有关于选择性提示的建议,但还没有像这样的东西进入产品。

正如对该问题的评论所述,您现在最好的选择是使用实数表,而不是使用递归 CTE 即时生成一个。第二种选择是使用手动物化 - 一个临时表 - 我相信你知道。