Sol*_*zky 28

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

  1. 您甚至不需要查看执行计划就可以看到它们在不使用时被忽略。以下应该产生“除以 0”错误,但不会因为cte2根本没有被选中:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 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;
    
    Run Code Online (Sandbox Code Playgroud)

关于性能,未使用的 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
Run Code Online (Sandbox Code Playgroud)

当做完所有事情都没有执行时,就会出现一个问题:

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'.
*/
Run Code Online (Sandbox Code Playgroud)


Eri*_*ing 21

他们似乎没有这样做,但这实际上只适用于嵌套的 CTE。

创建两个临时表:

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

CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );
Run Code Online (Sandbox Code Playgroud)

查询 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;
Run Code Online (Sandbox Code Playgroud)

查询 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;
Run Code Online (Sandbox Code Playgroud)

查询计划:

坚果

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