我编写了一个表值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中相同的select查询,而是将其输出到临时表中。
引用CTE版本的版本需要40秒。引用临时表的版本需要1到2秒。
为什么SQL Server不够智能,无法将CTE结果保存在内存中?
我喜欢CTE,特别是在这种情况下,因为我的UDF是一个表值的,所以它允许我将所有内容保存在一个语句中。
要使用临时表,我需要编写一个值为UDF的多语句表,我找到了一个稍微不太优雅的解决方案。
你们中有些人对CTE有这种性能问题,如果是这样,你们是如何对它们进行排序的?
谢谢,
Kharlos
答案 0 :(得分:6)
我相信每次检索CTE结果。使用临时表,结果将被存储,直到被删除。这似乎可以解释您切换到临时表时所看到的性能提升。
另一个好处是您可以在临时表上创建索引,而这些索引是您无法对cte执行的。不确定在你的情况下是否会有好处但是很高兴知道。
相关阅读:
从上一个链接引用:
CTE的基础查询将是 每次引用时调用 紧接着的查询。
我会说使用临时表。不幸的是,优雅并不总是最好的解决方案。
<强>更新强>
嗯,这让事情变得更加困难。看着你的整个环境,我很难说。一些想法:
left join
,则可以将其移至索引视图中。如果你能够做到这一点,你甚至可以看到性能提升甚至超过临时表。