为什么将 CTE 写入实际表会提供性能改进?

Jam*_*mes 3 sql-server-2008 sql-server

我是 CTE 的忠实粉丝。他们是最棒的。你知道,我知道,我们都知道。

今天,如果将 CTE 写入真实表,我的查询速度会提高 1000 倍左右。

查询本身在链接中,但我怀疑它并不重要。为简化起见,我需要一份保险单中所有索赔的清单,其中一项索赔满足特定条件(cat = 1)。

简化版本如下所示:

    with cat_claims as (select distinct pol_nbr from claims where cat = 1)

    select * from claims c
    inner join policies p on p.polnbr = c.polnbr
    inner join cat_claims cat on c.pol_nbr= cat.pol_nbr
Run Code Online (Sandbox Code Playgroud)

这是超级骗子慢。如果我重写它看起来像这样:

    with cat_claims as (select distinct claim_nbr from claims where cat = 1)

    select * into cat_claims
    from cat_claims

    select * from claims c
    inner join policies p on p.polnbr = c.polnbr
    inner join cat_claims cat on c.pol_nbr = cat.pol_nbr

    drop table cat_claims
Run Code Online (Sandbox Code Playgroud)

它的方式,方式更快。这对我来说很奇怪。

问题为什么将 CTE 写入实际表会提供性能改进?

慢计划:

https://www.brentozar.com/pastetheplan/?id=HkyrpAtH4

快速计划:

https://www.brentozar.com/pastetheplan/?id=ByGkAAKrN

Ran*_*gen 6

即使他们是估计的计划,而不是实际的,所不同的我看起来是从连接表来: RATING_CONTRIB_LOSSRATING_CONTRIB_USRcmbgrprating_revision_set,和Rating从参数表CTE

加入这些表会产生巨大的表线轴(懒惰线轴),在实际计划中可能会更大。

在此处输入图片说明

对于嵌套循环运算符顶部的每一行,扫描底部结果集,导致

3361 行 * 第 3359 行table spool--> Nested loops-->Top(1)

1541 行 * 3359 在第二个table spool--> Nested loops-->Top(1)

在此处输入图片说明

这些表假脱机不存在于临时表查询的两个计划中的任何一个中。

在此处输入图片说明

我得到的是,插入到表中的查询部分与CTE. 有一些差异,但我认为减速的主要原因是由于工作台线轴。

差的一个例子是,在全表扫描 RATING_CONTRIB_LOSSCTE计划改变为扫描与所述临时表中计划的残余谓词。

CTE

在此处输入图片说明

临时表

在此处输入图片说明 在此处输入图片说明

您可能会争辩说,使用临时表的查询的第二部分可能会RATING_CONTRIB_LOSS再次访问该表。我不得不同意,因为它将进行昂贵的密钥查找。

在此处输入图片说明

但是CTE计划中的第二个表假脱机也基于与RATING_CONTRIB_LOSS表的嵌套循环连接,临时表计划中没有,这是一个很大的优势。

结论

我确实相信执行时间的差异来自使用临时表结果的查询,而不会使用昂贵的运算符。更具体地说是表线轴。我需要 100% 确定实际计划。

作为旁注,在许多情况下,将 CTE 重写为临时表或仅拆分查询通常可以提供更好的执行时间。

猜测一个装有苹果和梨的袋子里有多少苹果比猜测一个装有苹果、梨、芒果、橙子、桃子……(是的水果 = 桌子)的袋子里有多少苹果更容易。

我还会使用实际的 #temp 表来存储您的中间结果。


比较 CTE & Temp 表插入查询部分

使用 CTE 第 1 部分进行计划

在此处输入图片说明

计划与 CTE 第 2 部分 在此处输入图片说明

计划与临时表第 1 部分 在此处输入图片说明

RATING_CONTRIB_LOSS & Hash match join <> Nested Loops Join 的区别。休息非常相似,评级表上的相同操作(2x)