Was ist der Unterschied zwischen WITH CTE und WITH CTE (<Spaltennamen>)?

11

Wie unter Verwenden allgemeiner Tabellenausdrücke in MSDN gezeigt, können Sie einen CTE wie folgt definieren:

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

und benutze es wie:

SELECT <column_list> FROM expression_name;

Angenommen, ich habe folgende 2 CTEs

with cte1 as(
select name from Table1
)

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

Eine Abfrage gibt für beide CTEs dieselben Ergebnisse aus wie die innere Abfrage. Der einzige Unterschied zwischen diesen beiden besteht darin, dass in cte2 der Spaltenname ( (name)) in seiner Deklaration definiert ist.

Wenn ich beide CTEs ausführe, sehe ich keinen Unterschied im Ausführungsplan.

Ich bin nur neugierig zu wissen:

  • Welchen Unterschied macht es, wenn ich in der CTE-Definition keine Spaltennamen spezifiziere?
  • Warum sollte / sollte ich beim Erstellen von CTE keine Spaltennamen angeben?
  • Beeinflusst es zufällig den Ausführungsplan für Abfragen? (Soweit ich gesehen habe, macht es keinen Unterschied.)
Ketan
quelle

Antworten:

25

Sie haben fast schon die Antwort auf eine Ihrer Fragen.

Auf der MSDN- Seite befindet sich direkt nach Ihrem Angebot eine Zeile, die dies erklärt:

Die grundlegende Syntaxstruktur für einen CTE lautet:

WITH Ausdrucksname [(Spaltenname [, ... n])]

WIE

(CTE_query_definition)

Die Liste der Spaltennamen ist nur optional, wenn in der Abfragedefinition eindeutige Namen für alle resultierenden Spalten angegeben sind.

(Betonung hinzugefügt)

Dies würde bedeuten, dass Sie in einigen Situationen Spaltennamen angeben müssen:

  • Das würde funktionieren:

    WITH [test_table] ([NoName], [CAST], [Function]) 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
  • wie würde das:

    WITH [test_table]  
    AS
    (
        SELECT 
            1 as [NoName]
          , CAST('1' AS CHAR(1)) as [CAST]
          , dbo.CastToChar(1) as [Function]
    )
    SELECT * FROM [test_table];
  • Dies wäre jedoch nicht der Fall, da es keine eindeutigen Namen für die Spalten gibt:

    WITH [test_table] 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
Shaneis
quelle
1
Im Wesentlichen ist die Version ohne Spalten dieselbe wie die Version mit Spalte, außer dass SQL die Spaltennamen aus der Abfrage "ableiten" muss.
KutuluMike
10

Anekdotisch bevorzuge ich es, die Spalten innerhalb des CTE anstelle der WITH CTE (xxx) AS1- Klausel zu benennen , da Sie niemals versehentlich die Namen mit dem Spalteninhalt nicht übereinstimmen.

Nehmen Sie zum Beispiel das folgende Beispiel:

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

Was zeigt das an? Es zeigt den Inhalt der ySpalte unter der Überschrift von xund den Inhalt der xSpalte unter der Überschrift y.

Mit dieser Erkenntnis gebe ich niemals die Spaltennamen in der (xxx) ASKlausel an, sondern mache es so:

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

Dies beseitigt alle Zweifel an den Spaltendefinitionen.

Auf einer völlig unabhängigen Randnotiz; Geben Sie immer den Schemanamen an, wenn Sie auf Objektnamen verweisen , und beenden Sie Ihre Anweisungen mit einem Semikolon .

Max Vernon
quelle
7

Letztendlich benötigt jede Spalte einen gültigen Namen und Sie können ihn auf zwei Arten zuweisen:

  1. Spaltenliste

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
  2. Verwenden der ursprünglichen Spaltennamen oder Aliase

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

Wenn Sie sowohl Alias als auch Spaltenliste ausführen

  1. Sowohl Spaltenliste als auch Aliase

    ;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;

Dies ähnelt der Definition einer Ansicht oder einer abgeleiteten Tabelle, in der Sie auch eine Liste von Spaltennamen angeben können.

Spaltenliste : Wenn Sie viele komplexe Berechnungen haben, ist es einfacher, den Namen zu erkennen, da diese nicht im Quellcode verstreut sind. Und es ist einfacher, wenn Sie ein rekursives cte haben und Sie können zwei verschiedene Namen für dieselbe Spalte in # 3 zuweisen.

ursprünglicher Name / Aliase : Sie müssen nur dann einen Alias ​​zuweisen, wenn Sie eine Berechnung durchführen oder eine Spalte umbenennen möchten / müssen

dnoeth
quelle
1
"leichter zu erkennen" ist vielleicht etwas subjektiv. Ich bevorzuge es, die Spalten-Aliase am Zeilenanfang zu haben, wie in SomeAlias = SomeFunction(SomeColumn), mit nur einer Spaltendefinition pro Zeile. Dies ermöglicht einen einfachen Scan auf der linken Seite der Spaltenliste, um die gesuchte zu finden.
Max Vernon
1
@MaxVernon: Richtig, und das Hinzufügen von Leerzeilen zwischen Berechnungen, die mehrere Zeilen umfassen, hilft ebenfalls. Tatsächlich
lasse
2
Komisch, dass du Ansichten erwähnt hast. Ich habe die Spaltenliste nach dem Ansichtsnamen beim Definieren einer Ansicht nie wie in verwendet CREATE VIEW SomeView (ColA, ColB, …) AS …. Nachdem Sie das angesprochen haben, denke ich an Szenarien wie 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);- was für eine Freude das zu debuggen wäre!
Andriy M