我有一个存储过程,我在其中使用公用表表达式在菜单上构建分层路径(因此它可以显示类似父菜单 - >子菜单 - >子子菜单 - > ...)
它非常适合我想要使用它的问题,当我将从递归CTE获得的信息放入我真正想要的信息时会出现问题。我从我的数据到CTE进行内部连接并获得分层路径。对于返回约300行的内容,存储过程平均需要15-20秒。
当我将CTE的结果插入Temp Table并根据它进行连接时,该过程只需不到一秒钟。
我只是想知道为什么只使用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运行的确切时间并存储在内存中?我是在假设它被调用一次然后在内存中徘徊的情况下运行的,但在某些情况下可能被称为多次?
答案 0 :(得分:1)
区别在于CTE没有持久化,临时表是(至少对于会话而言)。加入非持久列意味着与已经预先评估的临时表中的同一列相比,SQL根本没有数据统计信息。基本上,临时表会缓存您将使用的内容,SQL Server可以更好地优化它。在连接函数或表变量的结果时会遇到相同的问题。
我的猜测是你的CTE执行计划是用一个线程执行的,而你的临时表可以使用多个线程。您可以通过在运行查询时包含实际执行计划并在每个运算符上查找指向相反方向的两个水平箭头来检查此问题。这表明了并行性。
P.S。 - 尝试设置“set statistics io on”和“set statistics time on”,以查看运行查询的实际成本是否相同,无论运行持续时间如何。