Qual é a diferença entre WITH CTE e WITH CTE (<column_names>)?

11

Conforme mostrado em Usando expressões comuns de tabela no MSDN, você pode definir um CTE como:

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

e use-o como:

SELECT <column_list> FROM expression_name;

Digamos que eu tenho 2 CTEs a seguir

with cte1 as(
select name from Table1
)

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

Uma consulta gera os mesmos resultados para os dois CTEs que a consulta interna é a mesma. A única diferença entre esses dois é que cte2 tem o nome da coluna ( (name)) definido em sua declaração.

Quando executo os dois CTEs, não vejo diferença no plano de execução.

Estou apenas curioso para saber:

  • Que diferença faz se eu não especificar nenhum nome de coluna na definição CTE?
  • Por que devo / não devo especificar os nomes das colunas ao criar o CTE?
  • Isso afeta o plano de execução da consulta por acaso? (Até onde eu vi, isso não faz nenhuma diferença.)
Ketan
fonte

Respostas:

25

Você já tem quase a resposta para uma de suas perguntas.

Na página MSDN , há uma linha diretamente após sua cotação que explica o seguinte:

A estrutura básica da sintaxe para um CTE é:

WITH nome_da_expressão [(nome_da_coluna [, ... n])]

COMO

(CTE_query_definition)

A lista de nomes de colunas é opcional apenas se nomes distintos para todas as colunas resultantes forem fornecidos na definição de consulta.

(Enfase adicionada)

Isso significaria que você precisaria especificar nomes de colunas em algumas situações:

  • Isso funcionaria:

    WITH [test_table] ([NoName], [CAST], [Function]) 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
  • como seria isso:

    WITH [test_table]  
    AS
    (
        SELECT 
            1 as [NoName]
          , CAST('1' AS CHAR(1)) as [CAST]
          , dbo.CastToChar(1) as [Function]
    )
    SELECT * FROM [test_table];
  • Mas isso não teria, pois não tem nomes distintos para as colunas:

    WITH [test_table] 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
Shaneis
fonte
11
essencialmente, a versão sem colunas é igual à versão com coluna, exceto que o SQL precisa "inferir" os nomes das colunas da consulta.
precisa saber é o seguinte
10

Curiosamente, prefiro nomear as colunas dentro da CTE em vez de dentro da cláusula WITH CTE (xxx) AS1 , pois você nunca inadvertidamente fará a correspondência entre os nomes e o conteúdo da coluna.

Tomemos, por exemplo, o seguinte exemplo:

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

O que isso exibe? Ele mostra o conteúdo da ycoluna sob o cabeçalho de xe o conteúdo da xcoluna sob o cabeçalho y.

Com essa realização, nunca especifico os nomes das colunas na (xxx) AScláusula, mas faço assim:

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

Isso remove todas as dúvidas sobre quais são as definições de coluna.

Em uma nota lateral totalmente não relacionada; sempre especifique o nome do esquema ao fazer referência a nomes de objetos e termine suas instruções com ponto e vírgula .

Max Vernon
fonte
7

Por fim, cada coluna precisa de um nome válido e você pode atribuí-lo de duas maneiras:

  1. Lista de colunas

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
  2. Usando nomes ou aliases de coluna originais

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

Quando você faz alias e lista de colunas

  1. Lista de colunas e aliases

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

Isso é semelhante à definição de uma Visualização ou Tabela Derivada, na qual você também pode especificar uma lista de nomes de colunas.

lista de colunas : quando você tem muitos cálculos complexos, é mais fácil identificar o nome, porque eles não estão espalhados pelo código-fonte. E é mais fácil se você tiver um cte recursivo e puder atribuir dois nomes diferentes para a mesma coluna no 3.

nome / aliases originais : você só precisa atribuir um alias se fizer um cálculo ou desejar / precisar renomear uma coluna

não
fonte
11
"mais fácil de detectar" é talvez um pouco subjetivo. Prefiro ter os aliases da coluna no início da linha, como em SomeAlias = SomeFunction(SomeColumn), com apenas uma definição de coluna por linha. Isso permite uma varredura simples no lado esquerdo da lista de colunas para localizar o que você está procurando.
Max Vernon
11
@MaxVernon: Isso mesmo, e adicionar linhas em branco entre cálculos abrangendo várias linhas também ajuda. Na verdade eu principalmente omitir a lista de colunas, também ...
dnoeth
2
Engraçado que você mencionou pontos de vista. Eu nunca usei a lista de colunas após o nome da exibição ao definir uma exibição, como em CREATE VIEW SomeView (ColA, ColB, …) AS …. Agora que você mencionou isso, estou pensando em cenários como 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);- que alegria em depurar isso seria!
Andriy M