查询中未使用的CTE是否会影响性能和/或更改生成的查询计划?

#1 楼

看来它们并没有,但实际上仅适用于嵌套的CTE。

创建两个临时表:

CREATE TABLE #t1 (id INT);
INSERT #t1 ( id )
VALUES ( 1 );

CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );


查询1 :

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM your_mom;


查询2:

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM also_your_mom;


查询计划:



有开销,但是查询的不必要部分很早就消除了(在这种情况下为解析;在更复杂的情况下为简化阶段),因此,额外的工作实际上是最少的,并且确实不会有助于潜在的昂贵的基于成本的优化。

#2 楼

为Erik +1,但想添加两件事(在注释中效果不佳):



您甚至无需查看执行计划即可看到它们在不使用时被忽略。以下应产生“被0除”错误,但不是由于根本没有选择cte2

;WITH cte1 AS
(
  SELECT 1 AS [Bob]
),
cte2 AS (
  SELECT 1 / 0 AS [Err]
  FROM cte1
)
SELECT *
FROM   cte1;



CTE可以忽略,即使它们是唯一的CTE,即使从中选择它们,从逻辑上来说,所有行都将被排除。以下是查询优化器提前知道CTE不能返回任何行的情况,因此它甚至不必费心执行它:

;WITH cte AS
(
  SELECT 1 / 0 AS [Bob]
)
SELECT TOP (1) [object_id]
FROM   sys.objects
UNION ALL
SELECT cte.[Bob]
FROM   cte
WHERE  1 = 0;



关于性能,对未使用的CTE进行了解析和编译(或至少在以下情况下进行了编译),因此不能100%忽略它,但是成本必须可以忽略不计,也不值得关注。 br />
仅分析时,没有错误:

SET PARSEONLY ON;

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET PARSEONLY OFF;
GO


当执行仅欠执行时的所有操作时,就会出现问题:

GO
SET NOEXEC ON;
GO

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET NOEXEC OFF;
GO
/*
Msg 207, Level 16, State 1, Line XXXXX
Invalid column name 'NotHere'.
*/


评论


希望我可以将多个答案标记为正确答案,但是Erik击败了您,获得了手枪抽奖。 :)但是您的答案也非常有用,非常感谢,谢谢!

– J.D.
19-3-2在2:25



如果CTE在视图中并且视图嵌套3次以上怎么办?优化器不会放弃并全部运行吗?

– Zikato
19年4月10日在13:20

@Zikato我不知道,但这是一个很好的问题。通过使用我在前两个示例中展示的“除以零”技巧创建视图,您应该能够轻松地建立测试。请让我知道结果,因为我现在对这种情况非常好奇:-)。

–所罗门·鲁兹基
19年4月10日在14:21

@SolomonRutzky公平地说,我确实对其进行了测试,但这并不是结论性的。我已经从您的cte示例中创建了一个视图并将其嵌套5次,但是由于它都是恒定扫描并且并不复杂,因此优化程序可以很好地处理它。我希望将来对其进行更全面的测试,并将其隐藏在更复杂的逻辑中。我会告诉你。

– Zikato
19年4月10日在14:26

@Zikato有趣。不知道什么会被认为是“复杂的”,但是是的,我的例子非常简单。当您说“嵌套5次”时,您是说在其他相互调用且深度为5的视图/过程中还是在子查询/ CTE中?我认为有可能嵌套足够多的级别可能会跳过它,但这不是由于未引用它,而是由于较高的嵌套级别未使用它,而假定是针对较低的级别。我已经看到了将NEWID()放入UDF视图中的窍门,由于优化程序将其缓存,因此可以从多个调用返回相同的值。

–所罗门·鲁兹基
19年4月10日在14:49