如果CTE在查询中定义并且从未使用过,它会发出声音吗?


Answers:


21

看来它们并没有,但实际上仅适用于嵌套的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;

查询计划:

坚果

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


28

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

  1. 您甚至不需要查看执行计划就可以发现不使用它们就会被忽略。以下应产生“被0除”错误,但并非完全cte2没有被选择:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
  2. 即使CTE是唯一的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%忽略它,但是成本必须可以忽略不计,也不值得关注。

仅解析时,没有错误:

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击败了您,获得了手枪抽奖。:)但是您的答案也非常有用,非常感谢,谢谢!
JD

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

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

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

@Zikato有趣。不知道什么会被认为是“复杂的”,但是是的,我的例子非常简单。当您说“嵌套5次”时,您是说在其他相互调用且深度为5的视图/过程中还是在子查询/ CTE中?我认为有可能嵌套足够多的级别可能会跳过它,但这不是由于未引用它,而是由于较高的嵌套级别未使用它,而假定是针对较低的级别。我已经看到在NEWID()视图中使用UDF 的技巧可以由于优化器将其缓存而从多个调用中返回相同的值。
所罗门·鲁兹基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.