web-dev-qa-db-ja.com

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

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の作成中に列名を指定する必要がある/すべきでないのはなぜですか?
  • 万が一クエリ実行プランに影響はありますか? (私が見た限りでは、何の違いもありません。)
11
Ketan

質問の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];
    
25
Shaneis

事例として、WITH CTE (xxx) AS内ではなく、CTE内で列に名前を付けることを好みます1 名前と列の内容を誤って一致させないためです。

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

;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列の内容が表示されます。

この実現により、私はnever列名を(xxx) AS句、代わりに次のようにします。

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

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

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

10
Max Vernon

最終的に、各列には有効な名前が必要であり、次の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つの異なる名前を割り当てることができれば、より簡単です。

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

7
dnoeth