Яка різниця між CTE і З CTE (<імена колонки>)?


11

Як показано у використанні загальних табличних виразів на 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, що і внутрішній запит. Єдина різниця між цими двома полягає в тому, що cte2 має ім'я стовпця ( (name)), визначене в його декларації.

Коли я виконую обидва CTE, я не бачу різниці в плані виконання.

Мені просто цікаво знати:

  • Яка різниця, якщо я не вказую жодного імені стовпця у визначенні CTE?
  • Чому я повинен / не повинен вказувати назви стовпців під час створення CTE?
  • Чи це впливає на план виконання запитів випадково? (Наскільки я бачив, це не має ніякої різниці.)

Відповіді:


25

Ви вже майже отримали відповідь на одне із своїх питань.

На сторінці MSDN після вашої цитати є рядок, що пояснює це:

Основна структура синтаксису для CTE:

З ім'ям_вираз [(ім'я стовпця [, ... п])]

ЯК

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

1
по суті, версія без стовпців - це те саме, що версія з стовпцем, за винятком того, що SQL має "виводити" назви стовпців із запиту.
KutuluMike

10

Анекдотично, я вважаю за краще називати стовпці всередині 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;

Що відображає цей показник? Він показує вміст 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;

Це знімає всі сумніви щодо того, що таке визначення стовпців.

На абсолютно незв'язаній сторонній ноті; завжди вказуйте ім'я схеми, посилаючись на імена об'єктів , а ваші оператори закінчуйте комою .


7

Зрештою, кожен стовпець потребує дійсного імені, і його можна призначити двома способами:

  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.

оригінальне ім’я / псевдоніми : Псевдонім потрібно призначити лише тоді, коли ви робите розрахунок або хочете / повинні перейменувати стовпець


1
"легше помітити", можливо, трохи суб'єктивно. Я вважаю за краще мати псевдоніми стовпців на початку рядка, як у SomeAlias = SomeFunction(SomeColumn), із лише одним визначенням стовпця на рядок. Це дозволяє просте сканування ліворуч зі списку стовпців, щоб знайти ту, яку ви шукаєте.
Макс Вернон

1
@MaxVernon: Правильно, і додавання порожніх рядків між розрахунками, що охоплюють кілька рядків, також допомагає. Насправді я в основному опускаю і список стовпців ...
dnoeth

2
Смішно, що ви згадали погляди. Я ніколи не використовував список стовпців після імені перегляду при визначенні перегляду, як у 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);- яка радість налагодити це!
Андрій М
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.