我正在处理这个我选择使用 CTE 的巨大查询。由于此查询的复杂性,我最终不得不在后续查询中多次重用中间查询结果。我面临的问题可以通过以下玩具代码进行总结:
WITH cte1 AS (
SELECT id, name
FROM Manager)
, cte2 AS (
SELECT id, name
FROM Employee
LEFT OUTER JOIN cte1 ON Employee.id = cte1.id)
SELECT *
FROM cte1
Run Code Online (Sandbox Code Playgroud)
我注意到在一个相当小的表中寻找异常数量的索引,该表在第一个查询中被查询,根据执行计划,占整个查询的大约 30%。这让我相信基表被多次查询,因为它在后续查询中被多次引用。
我的印象是 CTE 中的中间结果被缓存,直到最后的语句。阅读微软关于 CTE 的文档,我找不到任何理由不这么想。
中间 CTE 是否缓存用于WITH
语句中的多种用途?如果没有,有没有办法在不重写查询的情况下缓存它?
谢谢!
不,没有办法缓存 CTE;如果它被多次引用,它通常会被执行多次。如果您想避免这种情况,您可以使用 #temp 表来缓存结果。
在某些情况下,执行计划可能足够简单,以至于 CTE 只实现一次(这在技术上并不意味着它被“缓存”了),或者结果可以与假脱机一起重用。我手头没有任何例子,即使我有,你也不能依赖它总是如此。甚至像这样简单的事情:
CREATE TABLE dbo.OneTuple(id int PRIMARY KEY);
INSERT dbo.OneTuple(id) VALUES(1);
GO
;WITH cte AS
(
SELECT id FROM dbo.OneTuple
)
SELECT id FROM cte
UNION ALL
SELECT id FROM cte;
Run Code Online (Sandbox Code Playgroud)
产生两个相同的索引扫描:
另请参阅Stack Overflow 上的这个很棒的 Martin Smith 答案,并在此反馈项目中进行了讨论。