2

我有一个存储过程,我使用公用表表达式在菜单上构建分层路径(因此它可以显示类似父菜单 -> 子菜单 -> 子子菜单 -> ...)

它非常适合我想要使用它的用途,当我将从递归 CTE 获得的信息放入我真正想要的信息时,问题就出现了。我从我的数据到 CTE 进行内部联接,然后退出分层路径。对于返回约 300 行的内容,存储过程平均需要 15-20 秒。

当我将 CTE 的结果插入临时表并基于它进行连接时,该过程只需不到一秒钟的时间。

我只是想知道为什么只使用 CTE 加入需要这么长时间,或者我是否以某种方式滥用 CTE。

**编辑这本质上是存储过程

With Hierarchical_Path (Menu_ID, Parent_ID, Path) 

As
(
Select
    EM.Menu_Id, Parent_ID, 
            Convert(varchar(max), 
            EM.Description) as Path
From
    Menu EM
Where
--EM.Topic_No is null
    EM.Parent_ID = 0 and EM.Disabled = 0
Union All
Select  
    EM.Menu_ID,  
            EM.Parent_ID, 
            Convert(Varchar(max),E.Path + ' -> ' + EM.Description) as Path
From
    Menu EM
Inner Join
    Hierarchical_Path E
On
    EM.Parent_ID = E.Menu_ID    
)

SELECT distinct   
    EM.Description
    ,EMS.Path
FROM
    dbo.Menu em
INNER JOIN
    Hierarchical_Path EMS
ON
    EMS.Menu_ID = em.Menu_Id
    2 more INNER JOINs
    2 Left Joins
    WHERE Clause

当我像这样运行查询(加入 CTE)时,性能大约为 20 秒。

当我将 CTE 结果插入临时表并加入其中时,性能是瞬时的。

再把我的查询分开一点,它似乎挂在 where 子句上。我想我的问题更多的是 CTE 何时运行以及它是否存储在内存中?我在假设它被调用一次然后在内存中停留的假设下运行,但在某些情况下它可以被调用多次吗?

4

1 回答 1

1

不同之处在于 CTE 不持久,而临时表是(至少对于会话)。加入非持久列意味着 SQL 与已经预先评估的临时表中的同一列相比,根本没有数据统计信息。基本上,临时表缓存了您将使用的内容,SQL Server 可以更好地对其进行优化。在加入函数或表变量的结果时也会遇到同样的问题。

我的猜测是您的 CTE 执行计划使用单个线程执行,而您的临时表可以使用多个线程。您可以通过在运行查询时包含实际执行计划并在每个运算符上寻找两个指向相反方向的水平箭头来检查这一点。这表明并行性。

PS - 尝试设置“set statistics io on”和“set statistics time on”以查看运行查询的实际成本是否相同,无论运行持续时间如何。

于 2012-05-24T22:57:15.893 回答