WITH CTE 和 WITH CTE (<column_names>) 之间有什么区别?

Ket*_*tan 12 sql-server cte sql-server-2014

如在 MSDN 上使用公用表表达式中所示,您可以将 CTE 定义为:

WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
Run Code Online (Sandbox Code Playgroud)

并使用它:

SELECT <column_list> FROM expression_name;
Run Code Online (Sandbox Code Playgroud)

假设我有以下 2 个 CTE

with cte1 as(
select name from Table1
)

with cte2(name) as(
select name from Table1
)
Run Code Online (Sandbox Code Playgroud)

一个查询为两个 CTE 输出相同的结果,因为内部查询是相同的。这两者之间的唯一区别是cte2(name)在其声明中定义了列名( )。

当我执行两个 CTE 时,我看不到执行计划有任何不同。

我只是想知道:

  • 如果我不在 CTE 定义中指定任何列名,这有什么区别?
  • 为什么我应该/不应该在创建 CTE 时指定列名?
  • 它是否会影响查询执行计划?(就我所见,它没有任何区别。)

Sha*_*eis 29

您几乎已经找到了其中一个问题的答案。

MSDN页面中,您的引用后面有一行直接解释了这一点:

CTE 的基本语法结构是:

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

作为

( CTE_query_definition )

仅当查询定义中提供了所有结果列的不同名称时,列名列表才是可选的。

(强调)

这意味着您需要在以下几种情况下指定列名:


Han*_*non 12

有趣的是,我更喜欢在 CTE 内而不是在WITH CTE (xxx) AS1子句内命名列,因为您永远不会无意中将名称与列内容不匹配。

以下面的例子为例:

;WITH MyCTE (x, y)
AS 
(
    SELECT mt.y
         , mt.x
    FROM MySchema.MyTable mt
)
SELECT MyCTE.x
     , MyCTE.y
FROM MyCTE;
Run Code Online (Sandbox Code Playgroud)

这显示什么?它显示的内容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;
Run Code Online (Sandbox Code Playgroud)

这消除了对列定义是什么的所有疑问。

在一个完全不相关的旁注上;在引用对象名称时始终指定模式名称,并以分号结束您的语句


dno*_*eth 8

最终,每一列都需要一个有效的名称,您可以通过两种方式分配它:

  1. 列列表

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用原始列名或别名

    ;WITH cte
    AS
    ( select col from tab )
    select col from cte;
    
    Run Code Online (Sandbox Code Playgroud)

当您同时执行别名列列表时

  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;
    
    Run Code Online (Sandbox Code Playgroud)

这类似于视图或派生表的定义,您也可以在其中指定列名列表。

列列表:当您有大量复杂的计算时,更容易找到名称,因为它们不会分散在源代码中。如果你有一个递归的 cte 并且你可以为 #3 中的同一列分配两个不同的名称,那就更容易了。

原始名称/别名:如果您进行计算或想要/必须重命名列,则只需分配别名

  • 有趣的是你提到了意见。在定义视图时,我从未在视图名称之后使用列列表,如在“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);` – 调试起来真是太开心了! (2认同)