【问题标题】:Joining Onto CTE Performance加入 CTE 性能
【发布时间】:2012-04-25 22:16:08
【问题描述】:

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

它非常适合我想用它来做的事情,当我将从递归 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 何时运行以及它是否存储在内存中?我在假设它被调用一次然后在内存中停留的假设下运行,但在某些情况下它可以被调用多次吗?

【问题讨论】:

  • 请在此处显示可比较的查询。
  • 递归 CTE 相对较慢,可能涉及多个表扫描。除非您提供有关 CTE 所涉及的表的更多详细信息以及这些表中的大致行数,否则我认为不可能回答您的问题
  • @a1ex07 你通常不能这么说。非递归 CTE 等效于视图。它在查询编译期间完全消失。在递归的情况下,它完全取决于你在做什么。
  • 如果您发布 where 子句或其余的连接,它可能更有意义。在我看来,你在 CTE 中所做的一切都很正常。

标签: sql sql-server performance common-table-expression


【解决方案1】:

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

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

附: - 尝试设置“set statistics io on”和“set statistics time on”,看看运行查询的实际成本是否相同,无论运行时间长短。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 2021-03-24
    相关资源
    最近更新 更多