web-dev-qa-db-ja.com

CTEがクエリで定義されており、使用されていない場合は、CTEは音を立てますか?

クエリ内の未使用のCTEはパフォーマンスに影響するか、生成されたクエリプランを変更しますか?

32
J.D.

表示されないようですが、これは実際にはネストされたCTEにのみ適用されます。

2つの一時テーブルを作成します。

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;

クエリプラン:

NUTS

オーバーヘッドはありますが、クエリの不要な部分は非常に早い段階で削除されます(この場合、解析中、より複雑なケースでは簡略化段階)。そのため、追加の作業は本当に最小限であり、コストがかかる可能性のあるコストに寄与しません。最適化。

21
Erik Darling

Erikに+1しましたが、次の2つを追加したいと思いました(コメントではうまくいきませんでした)。

  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である場合でも、また、それらが選択される場合でも無視できますif論理的にすべての行が除外されます。以下は、クエリオプティマイザーが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'.
*/
28
Solomon Rutzky