Apa perbedaan antara WITH CTE & WITH CTE (<column_names>)?

11

Seperti yang ditunjukkan dalam Menggunakan Common Table Expressions pada MSDN, Anda dapat mendefinisikan CTE sebagai:

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

dan gunakan seperti:

SELECT <column_list> FROM expression_name;

Katakanlah saya sudah mengikuti 2 CTE

with cte1 as(
select name from Table1
)

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

Kueri menampilkan hasil yang sama untuk kedua CTE karena kueri dalam sama. Satu-satunya perbedaan antara keduanya adalah bahwa cte2 memiliki nama kolom ( (name)) yang didefinisikan dalam deklarasi.

Ketika saya menjalankan kedua CTE, saya tidak melihat perbedaan dalam rencana eksekusi.

Saya hanya ingin tahu:

  • Apa bedanya jika saya tidak menentukan nama kolom dalam definisi CTE?
  • Mengapa saya harus / tidak menentukan nama kolom saat membuat CTE?
  • Apakah ini memengaruhi rencana eksekusi permintaan? (Sejauh yang saya lihat, tidak ada bedanya.)
Ketan
sumber

Jawaban:

25

Anda hampir memiliki jawaban untuk salah satu pertanyaan Anda.

Di halaman MSDN , ada garis langsung setelah kutipan Anda yang menjelaskan ini:

Struktur sintaksis dasar untuk CTE adalah:

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

SEBAGAI

(CTE_query_definition)

Daftar nama kolom adalah opsional hanya jika nama berbeda untuk semua kolom yang dihasilkan disediakan dalam definisi permintaan.

(Penekanan ditambahkan)

Ini berarti bahwa Anda perlu menentukan nama kolom dalam beberapa situasi:

  • Ini akan berhasil:

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

    WITH [test_table]  
    AS
    (
        SELECT 
            1 as [NoName]
          , CAST('1' AS CHAR(1)) as [CAST]
          , dbo.CastToChar(1) as [Function]
    )
    SELECT * FROM [test_table];
  • Tetapi ini tidak akan karena tidak memiliki nama yang berbeda untuk kolom:

    WITH [test_table] 
    AS
    (
        SELECT 
            1
          , CAST('1' AS CHAR(1))
          , dbo.CastToChar(1)
    )
    SELECT * FROM [test_table];
Shaneis
sumber
1
pada dasarnya, versi tanpa kolom sama dengan versi dengan kolom kecuali SQL harus "menyimpulkan" nama kolom dari kueri.
KutuluMike
10

Secara anekdot, saya lebih suka memberi nama kolom di dalam CTE daripada di dalam klausa WITH CTE (xxx) AS1 karena Anda tidak akan pernah secara tidak sengaja salah mencocokkan nama vs isi kolom.

Ambil contoh sebagai berikut:

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

Apa yang ditampilkan ini? Ini menunjukkan isi ykolom di bawah judul x, dan isi xkolom di bawah judul y.

Dengan realisasi ini, saya tidak pernah menentukan nama kolom dalam (xxx) ASklausa, sebaliknya saya melakukannya seperti ini:

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

Ini menghilangkan semua keraguan tentang apa definisi kolom itu.

Pada nota yang sama sekali tidak terkait; selalu tentukan nama skema saat merujuk nama objek , dan akhiri pernyataan Anda dengan tanda titik koma .

Max Vernon
sumber
7

Pada akhirnya setiap kolom membutuhkan nama yang valid dan Anda dapat menetapkannya dengan dua cara:

  1. Daftar kolom

    ;WITH cte (foo)
    AS
    ( select col from tab )
    select foo from cte;
  2. Menggunakan nama atau alias kolom asli

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

Ketika Anda melakukan kedua alias dan daftar kolom

  1. Keduanya daftar kolom & 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;

Ini mirip dengan definisi Lihat atau Tabel yang Diperoleh, tempat Anda dapat menentukan daftar nama kolom juga.

daftar kolom : Ketika Anda memiliki banyak perhitungan yang kompleks, lebih mudah untuk menemukan nama, karena mereka tidak tersebar melalui kode sumber. Dan lebih mudah jika Anda mendapat cyt rekursif dan Anda dapat menetapkan dua nama berbeda untuk kolom yang sama di # 3.

nama asli / alias : Anda hanya perlu menetapkan alias jika Anda melakukan perhitungan atau ingin / harus mengganti nama kolom

dnoeth
sumber
1
"lebih mudah dikenali" mungkin agak subyektif. Saya lebih suka memiliki alias kolom di awal baris, seperti pada SomeAlias = SomeFunction(SomeColumn), dengan hanya satu definisi kolom per baris. Ini memungkinkan pemindaian sederhana di sisi kiri daftar kolom untuk menemukan yang Anda cari.
Max Vernon
1
@ MaxVernon: Itu benar, dan menambahkan garis kosong antara perhitungan yang mencakup beberapa baris juga membantu. Bahkan saya kebanyakan menghilangkan daftar kolom, juga ...
dnoeth
2
Lucu bahwa Anda menyebutkan pandangan. Saya tidak pernah menggunakan daftar kolom setelah nama tampilan ketika mendefinisikan tampilan, seperti pada CREATE VIEW SomeView (ColA, ColB, …) AS …. Sekarang setelah Anda membahasnya, saya memikirkan skenario seperti 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);- betapa menyenangkannya debug itu!
Andriy M