WITH CTEとWITH CTE(<column_names>)の違いは何ですか?


11

MSDNの共通テーブル式の使用に示すように、CTEを次のように定義できます。

WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )

そしてそれを次のように使用します:

SELECT <column_list> FROM expression_name;

次の2つのCTEがあるとします

with cte1 as(
select name from Table1
)

with cte2(name) as(
select name from Table1
)

クエリは、内部クエリが同じであるため、両方のCTEに対して同じ結果を出力します。これら2つの違いは、cte2の(name)宣言で列名()が定義されていることだけです。

両方のCTEを実行しても、実行計画に違いはありません。

私は知りたいだけです:

  • CTE定義で列名を指定しない場合、どのような違いがありますか?
  • CTEの作成時に列名を指定する必要がある/すべきではないのはなぜですか?
  • 万が一クエリ実行プランに影響はありますか?(私が見た限りでは、何の違いもありません。)

回答:


25

質問の1つに対する回答はもうすぐです。

MSDNのページ、これを説明して直接あなたの引用の後に行があります:

CTEの基本的な構文構造は次のとおりです。

WITH式名[(列名[、... n])]

なので

(CTE_query_definition)

列名のリストは、結果のすべての列の個別の名前がクエリ定義で指定されている場合にのみオプションです。

(エンファシスを追加)

これは、いくつかの状況で列名を指定する必要があることを意味します。

  • これはうまくいくでしょう:

    WITH [test_table] ([NoName], [CAST], [Function]) 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
  • これと同じように:

    WITH [test_table]  
    AS
    (
        SELECT 
            1 as [NoName]
          , CAST('1' AS CHAR(1)) as [CAST]
          , dbo.CastToChar(1) as [Function]
    )
    SELECT * FROM [test_table];
  • ただし、列に個別の名前がないため、これはできません。

    WITH [test_table] 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];

1
基本的に、列のないバージョンは、SQLがクエリから列名を「推測」する必要があることを除いて、列のあるバージョンと同じです。
KutuluMike 2016

10

偶然にも、名前と列の内容を誤って一致させることがないため、WITH CTE (xxx) AS1句の内部ではなく、CTE内で列に名前を付けることをお勧めします。

たとえば、次の例を見てください。

;WITH MyCTE (x, y)
AS 
(
    SELECT mt.y
         , mt.x
    FROM MySchema.MyTable mt
)
SELECT MyCTE.x
     , MyCTE.y
FROM MyCTE;

これは何を表示していますか?yの見出しの下の列の内容と、見出しの下の列xの内容がx表示されyます。

この認識により、節で列名を指定することはありません(xxx) AS。代わりに、次のようにします。

;WITH MyCTE
AS 
(
    SELECT Alias1 = mt.y
         , Alias2 = mt.x
    FROM MySchema.MyTable mt
)
SELECT MyCTE.Alias1
     , MyCTE.Alias2
FROM MyCTE;

これにより、列の定義が何であるかについての疑問がすべてなくなります。

まったく関係のない副次的注意について。常にオブジェクト名を参照するときにスキーマ名を指定し、セミコロンを使用してステートメントを終了します


7

最終的に、各列には有効な名前が必要であり、次の2つの方法で割り当てることができます。

  1. 列リスト

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
  2. 元の列名またはエイリアスを使用する

    ;WITH cte
    AS
    ( select col from tab )
    select col from cte;

エイリアス列リストの両方を行う場合

  1. 列リストとエイリアスの両方

    ;WITH cte (foo, bar)
    AS
    ( select col1         -- not valid in outer Select
             col2 as colx -- not valid in outer Select
      from tab )
    select foo, bar from cte;

これは、ビューまたは派生テーブルの定義に似ており、列名のリストも指定できます。

列リスト:複雑な計算がたくさんある場合、ソースコード全体に分散していないため、名前を見つけやすくなります。また、再帰的なcteがあり、#3の同じ列に2つの異なる名前を割り当てることができれば、より簡単です。

元の名前/エイリアス:計算を行う場合、または列の名前を変更する必要がある場合にのみ、エイリアスを割り当てる必要があります


1
「見つけるのが簡単」は、おそらく少し主観的です。のように、列のエイリアスを行の先頭に配置し、SomeAlias = SomeFunction(SomeColumn)1行に1つの列定義のみを指定します。これにより、列リストの左側を下にスキャンするだけで、探している列を見つけることができます。
Max Vernon

1
@MaxVernon:そうです。複数行にわたる計算の間に空白行を追加することも役立ちます。実際、私も列リストをほとんど省略しています...
dnoeth

2
あなたが意見を述べたことはおかしい。のように、ビューを定義するときに、ビュー名の後に列リストを使用したことはありませんCREATE VIEW SomeView (ColA, ColB, …) AS …。あなたがそれを取り上げたので、私は次のようなシナリオを考えていますCREATE VIEW MyView (G) AS WITH cte (C) AS (SELECT A AS B FROM MyTable) SELECT E AS F FROM (SELECT C AS D FROM cte) AS s (E);–デバッグするのはなんと楽しいことでしょう!
Andriy M
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.