12

我有一个复杂的查询,需要在后续查询(实际上是更新语句)中使用。我已经尝试过使用 CTE 和临时表。与临时表方法相比,使用 CTE 的性能很糟糕。它类似于 15 秒与毫秒。为了简化测试而不是在后续查询中加入 CTE/Temp 表,我只是从中选择了 *。在这种情况下,它们的表现相同。

我已经在后续查询中查看了这两种方法的执行计划,然后只需选择 *. 使用简单选择,查询计划大致相同,但使用后续选择中的连接,查询计划则不同。具体来说,用于创建和填充临时表的查询计划部分保持不变,而用于创建和填充 CTE 的查询计划部分在随后用于带有连接的查询时会发生巨大变化。

我的问题是,为什么 CTE 的创建和填充的查询计划会随着它随后的使用方式而改变,而临时表却没有。同样在什么情况下,CTE 会比临时表产生更好的性能?

*注意我也使用了一个表变量,它与临时表方法相当。

谢谢

4

4 回答 4

13

CTE只是查询的别名。

每次使用时都可能(也可能不会)重新运行。

没有干净的方法来强制CTE实现SQL Server(如 Oracle's /*+ MATERIALIZE */),你必须做这样的肮脏把戏:

CTE如果在只需要一次评估(如 等)的计划中使用,则可能会提高HASH JOIN性能MERGE JOIN

在这些情况下,哈希表将直接从 构建CTE,而使用临时表将需要评估CTE,将结果拉入临时表并再次读取临时表。

于 2009-10-07T14:31:42.360 回答
9

你问的是一个复杂的问题,所以你会得到一个复杂的答案:这取决于。(我讨厌这种反应)。

然而,说真的,它与优化器如何选择数据计划有关(你已经知道了);临时表或变量类似于永久结构,因为执行计划将首先执行与填充该结构相关的操作,然后在后续操作中使用该结构。CTE 不是临时表;在后续操作使用 CTE 之前,不会计算 CTE 的使用,因此使用会影响计划的优化方式。

CTE 是针对可重用性和维护问题而实施的,不一定是性能;但是,在许多情况下(如递归),它们的性能将优于传统的编码方法。

于 2009-10-07T14:38:36.697 回答
2

我发现通常重复的 CTE 不会提高性能。

因此,例如,如果您使用 CTE 填充表,然后在以后的查询中加入相同的 CTE,则没有任何好处。不幸的是,CTE 不是快照,它们实际上必须重复才能在两个单独的语句中使用,因此它们往往会被评估两次。

我经常使用内联 TVF(可能包含 CTE)代替 CTE,它允许正确重用,并且在我的 SP 中并不比 CTE 更好或更差。

此外,我还发现,如果第一步更改了统计信息,那么执行计划可能会很糟糕,以至于第二步的执行计划总是不准确的,因为它是在任何步骤运行之前进行评估的。

在这种情况下,我着眼于手动存储中间结果,确保它们被正确索引,并将进程拆分为多个 SP,并添加 WITH RECOMPILE 以确保以后的 SP 有适合他们实际操作的数据的计划在。

于 2009-10-07T17:07:50.860 回答
1

我尝试使用从大表中选择的简单筛选器创建 CTE,然后对其进行 3 次子查询。

之后对临时表执行相同的操作。

结果是 CTE 耗时 70%,临时表耗时 -30%。因此临时表更适合该解决方案。

我不认为 CTE 只使用选定的查询创建一个临时表,而是 3 次选择一个大表。

于 2009-10-30T14:25:36.430 回答