Qual è la differenza tra WITH CTE e WITH CTE (<column_names>)?


11

Come mostrato in Utilizzo di espressioni di tabella comuni su MSDN, è possibile definire un CTE come:

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

e usalo come:

SELECT <column_list> FROM expression_name;

Diciamo che ho seguito 2 CTE

with cte1 as(
select name from Table1
)

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

Una query genera gli stessi risultati per entrambi i CTE poiché la query interna è la stessa. L'unica differenza tra questi due è che cte2 ha il nome di colonna ( (name)) definito nella sua dichiarazione.

Quando eseguo entrambi i CTE, non vedo alcuna differenza nel piano di esecuzione.

Sono solo curioso di sapere:

  • Che differenza fa se non specifico alcun nome di colonna nella definizione CTE?
  • Perché dovrei / non dovrei specificare i nomi delle colonne durante la creazione di CTE?
  • Influisce per caso sul piano di esecuzione delle query? (Per quanto ho visto, non fa alcuna differenza.)

Risposte:


25

Hai quasi già la risposta per una delle tue domande.

Nella pagina MSDN , c'è una riga subito dopo il tuo preventivo che spiega questo:

La struttura di sintassi di base per un CTE è:

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

COME

(CTE_query_definition)

L'elenco dei nomi di colonna è facoltativo solo se nella definizione della query vengono forniti nomi distinti per tutte le colonne risultanti.

(Enfasi aggiunta)

Ciò significherebbe che dovrai specificare i nomi delle colonne in alcune situazioni:

  • Questo funzionerebbe:

    WITH [test_table] ([NoName], [CAST], [Function]) 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
    
  • come sarebbe questo:

    WITH [test_table]  
    AS
    (
        SELECT 
            1 as [NoName]
          , CAST('1' AS CHAR(1)) as [CAST]
          , dbo.CastToChar(1) as [Function]
    )
    SELECT * FROM [test_table];
    
  • Ma questo non sarebbe in quanto non ha nomi distinti per le colonne:

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

1
in sostanza, la versione senza colonne è la stessa della versione con colonna, tranne SQL deve "inferire" i nomi delle colonne dalla query.
KutuluMike,

10

Aneddoticamente, preferisco dare un nome alle colonne all'interno del CTE anziché all'interno della clausola WITH CTE (xxx) AS1 poiché non inavvertitamente non corrisponderai ai nomi rispetto al contenuto della colonna.

Prendi ad esempio il seguente esempio:

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

Cosa visualizza questo? Mostra il contenuto della ycolonna sotto l'intestazione di xe il contenuto della xcolonna sotto l'intestazione y.

Con questa realizzazione, non ho mai specificato i nomi delle colonne nella (xxx) ASclausola, invece lo faccio in questo modo:

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

Ciò elimina tutti i dubbi su quali siano le definizioni di colonna.

Su una nota a margine totalmente indipendente; specificare sempre il nome dello schema quando si fa riferimento ai nomi degli oggetti e terminare le istruzioni con un punto e virgola .


7

Alla fine ogni colonna ha bisogno di un nome valido e puoi assegnarlo in due modi:

  1. Elenco di colonne

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
  2. Utilizzo di nomi o alias di colonne originali

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

Quando esegui sia l' alias che l' elenco delle colonne

  1. Elenco di colonne e alias

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

Questo è simile alla definizione di una vista o di una tabella derivata, in cui è possibile specificare anche un elenco di nomi di colonne.

elenco di colonne : quando hai molti calcoli complessi è più facile individuare il nome, perché non sono sparsi attraverso il codice sorgente. Ed è più facile se hai un cte ricorsivo e puoi assegnare due nomi diversi per la stessa colonna in # 3.

nome / alias originali : devi assegnare un alias solo se fai un calcolo o desideri / devi rinominare una colonna


1
"più facile da individuare" è forse un po 'soggettivo. Preferisco avere gli alias di colonna all'inizio della riga, come in SomeAlias = SomeFunction(SomeColumn), con una sola definizione di colonna per riga. Ciò consente una semplice scansione nella parte sinistra dell'elenco delle colonne per individuare quella che stai cercando.
Max Vernon,

1
@MaxVernon: Esatto, e anche l'aggiunta di righe vuote tra i calcoli su più righe aiuta. In realtà, per lo più,
ometto

2
Divertente che tu abbia menzionato le opinioni. Non ho mai usato l'elenco delle colonne dopo il nome della vista durante la definizione di una vista, come in CREATE VIEW SomeView (ColA, ColB, …) AS …. Ora che l'hai sollevato, sto pensando a scenari come 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);: che gioia sarebbe il debug!
Andriy M,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.