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)
估计
实际的
运行时间为 23 毫秒,但仅估计最终输出的一行(仅第一个视图的 2 行)。
问题是,当它被用作连接真实数据的子查询时(例如通过“DaysAgo”),计划通常是一个非常慢的嵌套循环,我经常需要添加连接提示/反向顺序等。
无论如何在保持 CTE 方法的同时改进估计?是否曾经请求过“with (AssumeMinRows=N)”提示?对于许多情况(不仅仅是 CTE),这似乎是一个很好的通用助手。
为什么递归 CTE 估计只有 1 行?
递归公用表表达式的基数估计非常有限。
在原始基数估计模型下,估计值是锚点和递归部分的基数估计值的简单总和。这相当于假设递归部分只执行一次。
在 SQL Server 2014 中,启用新的基数估计模型后,逻辑略有更改,以假设递归部分执行 3 次,每次迭代返回的行数相同。
这两个都是未经训练的猜测,因此使用递归 CTes 通常会导致质量较差的估计也就不足为奇了。更一般地说,估计递归过程的结果几乎是不可能的,因此优化器甚至不会尝试。这不会通过使用特别简单的递归结构来改变,显然是为了产生序列号——优化器没有检测这种模式的逻辑。
在您的特定情况下,最终估计是一个,因为优化器进一步猜测谓词的选择性,例如[Recr1007]<(100)
(在节点 ID 3 处的过滤器中)和([Recr1003]*(100)+[Recr1007])<=(10000)
(节点 ID 2 处的嵌套循环连接上的残差谓词)。同样,这些都是猜测,结果令人遗憾,但并不令人惊讶。
无论如何在保持 CTE 方法的同时改进估计?
不是我所知道的。
是否曾经请求过“with (AssumeMinRows=N)”提示?
不是直接在这些方面。有很多要求实现CTE,如果这种实现带有自动统计数据生成,这将有所帮助。我不会列出其他人,因为您似乎已经对大多数建议发表了评论:)
也有关于选择性提示的建议,但还没有像这样的东西进入产品。
正如对该问题的评论所述,您现在最好的选择是使用实数表,而不是使用递归 CTE 即时生成一个。第二种选择是使用手动物化 - 一个临时表 - 我相信你知道。