9

我编写了一个表值 UDF,它以 CTE 开头,以从大表中返回行的子集。CTE 中有几个连接。几个内连接和一个左连接到其他表,这些表不包含很多行。CTE 有一个 where 子句,它返回一个日期范围内的行,以便只返回需要的行。

然后,我在 4 个自左连接中引用此 CTE,以便使用不同的标准构建小计。

查询非常复杂,但这里是它的简化伪版本

WITH DataCTE as
(
     SELECT [columns] FROM table
                      INNER JOIN table2
                      ON [...]

                      INNER JOIN table3
                      ON [...]

                      LEFT JOIN table3
                      ON [...]
)
SELECT [aggregates_columns of each subset] FROM DataCTE Main
LEFT JOIN DataCTE BananasSubset
               ON [...] 
             AND Product = 'Bananas'
             AND Quality = 100
LEFT JOIN DataCTE DamagedBananasSubset
               ON [...]
             AND Product = 'Bananas'
             AND Quality < 20
LEFT JOIN DataCTE MangosSubset
               ON [...]
GROUP BY [

我感觉 SQL Server 感到困惑并为每个自连接调用 CTE,这似乎通过查看执行计划得到证实,尽管我承认不是阅读这些的专家。

我会假设 SQL Server 足够聪明,只能从 CTE 执行一次数据检索,而不是多次执行。

我尝试了相同的方法,但没有使用 CTE 来获取数据子集,而是使用了与 CTE 中相同的选择查询,但将其输出到临时表。

引用 CTE 版本的版本需要 40 秒。引用临时表的版本需要 1 到 2 秒。

为什么 SQL Server 不够智能,无法将 CTE 结果保存在内存中?

我喜欢 CTE,尤其是在这种情况下,因为我的 UDF 是一个表值的,所以它允许我将所有内容保存在一个语句中。

要使用临时表,我需要编写一个值为 UDF 的多语句表,我发现它是一个稍微不那么优雅的解决方案。

你们中的一些人是否在 CTE 中遇到过这种性能问题,如果有,你们是如何解决这些问题的?

谢谢,

卡洛斯

4

1 回答 1

7

我相信每次都会检索到 CTE 结果。使用临时表,结果将被存储,直到它被删除。这似乎可以解释您在切换到临时表时看到的性能提升。

另一个好处是您可以在临时表上创建索引,而您不能对 cte 执行此操作。不确定您的情况是否有好处,但很高兴知道。

相关阅读:

引用最后一个链接:

每次在紧随其后的查询中引用 CTE 的基础查询时,都会调用它。

我会说去临时表。不幸的是,优雅并不总是最好的解决方案。

更新:

嗯,这让事情变得更加困难。如果不看看你的整个环境,我很难说。

一些想法:

  • 您可以使用存储过程而不是 UDF(而不是从内部)吗?
  • 这可能是不可能的,但如果您可以left join从您的 CTE 中删除,您可以将其移动到索引视图中。如果您能够做到这一点,您甚至可以看到临时表的性能提升。
于 2010-06-16T15:45:53.393 回答