Mana yang lebih berkinerja, CTE atau tabel sementara?

Jawaban:

64

Saya akan mengatakan mereka adalah konsep yang berbeda tetapi tidak terlalu berbeda untuk mengatakan "kapur dan keju".

  • Tabel temporer baik untuk digunakan kembali atau untuk melakukan beberapa proses lewat pada satu set data.

  • CTE dapat digunakan untuk mengulang atau sekadar meningkatkan keterbacaan.
    Dan, seperti tampilan atau tabel sebaris fungsi nilai juga bisa diperlakukan seperti makro untuk diperluas di kueri utama

  • Tabel temp adalah tabel lain dengan beberapa aturan seputar cakupan

Saya telah menyimpan procs tempat saya menggunakan keduanya (dan variabel tabel juga)

gbn
sumber
13
Tabel suhu juga memungkinkan untuk Indeks dan bahkan Statistik yang terkadang diperlukan, sedangkan CTE tidak.
CodeCowboyOrg
10
Saya pikir jawaban ini tidak cukup menyoroti fakta bahwa CTE dapat menyebabkan kinerja yang buruk. Saya biasanya merujuk jawaban ini di dba.stackexchange. Pertanyaan Anda muncul di urutan kedua di mesin pencari saya jika saya mencari cte vs temporary tablesjadi IMHO jawaban ini perlu menyoroti kekurangan CTE dengan lebih baik. TL; DR dari jawaban yang ditautkan: CTE tidak boleh digunakan untuk kinerja. . Saya setuju dengan kutipan itu karena saya telah mengalami kelemahan CTE.
TT.
3
@TT. Menarik. Saya menemukan bahwa CTE bekerja jauh lebih baik
Squ1rr3lz
204

Tergantung.

Pertama-tama

Apa itu Ekspresi Tabel Umum?

CTE (non rekursif) diperlakukan sangat mirip dengan konstruksi lain yang juga dapat digunakan sebagai ekspresi tabel sebaris di SQL Server. Tabel turunan, Tampilan, dan fungsi nilai tabel sebaris. Perhatikan bahwa sementara BOL mengatakan bahwa CTE "dapat dianggap sebagai kumpulan hasil sementara" ini adalah deskripsi yang sepenuhnya logis. Lebih sering daripada tidak itu tidak materlialized dengan sendirinya.

Apa itu tabel sementara?

Ini adalah kumpulan baris yang disimpan pada halaman data di tempdb. Halaman data mungkin sebagian atau seluruhnya berada dalam memori. Selain itu, tabel sementara dapat diindeks dan memiliki statistik kolom.

Uji Data

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Contoh 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

Rencana 1

Perhatikan pada denah di atas tidak ada penyebutan CTE1. Itu hanya mengakses tabel dasar secara langsung dan diperlakukan sama seperti

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Menulis ulang dengan mewujudkan CTE menjadi tabel sementara menengah di sini akan menjadi sangat kontraproduktif.

Mewujudkan definisi CTE dari

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Akan melibatkan penyalinan sekitar 8GB data ke tabel sementara kemudian masih ada biaya tambahan untuk memilih darinya juga.

Contoh 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Contoh di atas membutuhkan waktu sekitar 4 menit di mesin saya.

Hanya 15 baris dari 1.000.000 nilai yang dihasilkan secara acak yang cocok dengan predikatnya, tetapi pemindaian tabel yang mahal terjadi 16 kali untuk menemukannya.

masukkan deskripsi gambar di sini

Ini akan menjadi kandidat yang baik untuk mewujudkan hasil antara. Penulisan ulang tabel suhu yang setara membutuhkan waktu 25 detik.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Dengan Plan

Materialisasi menengah dari bagian kueri menjadi tabel sementara kadang-kadang dapat berguna meskipun hanya dievaluasi sekali - jika memungkinkan sisa kueri untuk dikompilasi ulang dengan memanfaatkan statistik pada hasil yang terwujud. Contoh dari pendekatan ini ada di artikel SQL Cat When To Break Down Complex Queries .

Dalam beberapa keadaan SQL Server akan menggunakan spool untuk menyimpan hasil antara, misalnya dari CTE, dan menghindari keharusan untuk mengevaluasi kembali sub pohon itu. Hal ini dibahas dalam item Connect (bermigrasi) Memberikan petunjuk untuk memaksa perwujudan menengah dari CTE atau tabel turunan . Namun tidak ada statistik yang dibuat mengenai hal ini dan bahkan jika jumlah baris yang di-spool sangat berbeda dari perkiraan tidak mungkin untuk rencana eksekusi yang sedang berjalan untuk secara dinamis beradaptasi sebagai tanggapan (setidaknya dalam versi saat ini. Rencana Kueri Adaptif dapat menjadi mungkin di masa depan).

Martin Smith
sumber
34
Ini adalah satu-satunya jawaban yang menjawab pertanyaan sebenarnya (yaitu menanyakan mana yang kinerjanya lebih baik bukan apa bedanya atau mana yang menjadi favorit Anda), dan menjawab pertanyaan itu dengan benar: "Tergantung" adalah jawaban yang benar. Itu juga satu-satunya jawaban dengan data pendukung untuk dijelaskan, beberapa orang lain (dengan jumlah suara tinggi) membuat klaim yang pasti bahwa yang satu lebih baik dari yang lain tanpa referensi atau bukti ... Yang jelas, semua jawaban itu juga salah . Karena "Itu tergantung"
Arkaine55
3
Ini juga merupakan jawaban yang ditulis dengan baik dan direferensikan dengan baik. Serius.
Dan Williams
Saya suka menyoroti bagian ini yang menurut saya benar. Materialisasi menengah dari bagian kueri ke dalam tabel sementara terkadang dapat berguna meskipun hanya dievaluasi sekali
Mark Monforti
53

CTE memiliki kegunaannya - ketika data dalam CTE kecil dan ada peningkatan keterbacaan yang kuat seperti halnya pada tabel rekursif. Namun, kinerjanya tentu tidak lebih baik dari variabel tabel dan ketika seseorang berurusan dengan tabel yang sangat besar, tabel sementara secara signifikan mengungguli CTE. Ini karena Anda tidak dapat menentukan indeks pada CTE dan ketika Anda memiliki data dalam jumlah besar yang memerlukan penggabungan dengan tabel lain (CTE hanya seperti makro). Jika Anda menggabungkan beberapa tabel dengan jutaan baris rekaman di setiap tabel, CTE akan bekerja jauh lebih buruk daripada tabel sementara.

CSW
sumber
10
Saya telah melihat ini dari pengalaman saya sendiri. CTE bekerja lebih lambat secara signifikan.
goku_da_master
8
CTE juga bekerja lebih lambat karena hasilnya tidak disimpan dalam cache. Jadi setiap kali Anda menggunakan CTE, kueri, rencana, dan semuanya dijalankan kembali.
goku_da_master
2
Dan mesin db dapat memilih untuk menjalankan kembali kueri tidak hanya setiap referensi, tetapi untuk setiap baris kueri konsumen, sebagai subkueri yang berkorelasi ... Anda harus selalu berhati-hati jika tidak diinginkan.
Mike M
1
Tabel temp disimpan dalam tempdb di SQL Server, yang merupakan disk tetapi memiliki manfaat untuk diindeks dan pengoptimal SQL berfungsi dengan baik pada kueri pilihan dalam kasus tersebut. Tidak yakin di db atau area disk mana CTE disimpan (bila melebihi ukuran memori dan antri untuk paging IO) tetapi tidak pernah dioptimalkan dengan volume data yang besar. Saya telah menggunakan opsi kompiler (dengan kompilasi ulang) kadang-kadang untuk membuatnya lebih cepat
rmehra76
35

Tabel temp selalu ada di disk - selama CTE Anda dapat disimpan di memori, kemungkinan besar akan lebih cepat (seperti variabel tabel juga).

Tapi sekali lagi, jika pemuatan data CTE Anda (atau variabel tabel temp) menjadi terlalu besar, itu akan disimpan di disk juga, jadi tidak ada manfaat besar.

Secara umum, saya lebih suka CTE daripada tabel temp karena hilang setelah saya menggunakannya. Saya tidak perlu berpikir untuk menjatuhkannya secara eksplisit atau apa pun.

Jadi, tidak ada jawaban yang jelas pada akhirnya, tetapi secara pribadi, saya lebih suka CTE daripada tabel temp.

marc_s
sumber
3
Dalam kasus SQLite dan PostgreSQL, tabel sementara secara otomatis dihapus (biasanya di akhir sesi). Saya tidak tahu tentang DBMS lainnya.
Serrano
2
CTE seperti tampilan sementara. Data AFAIK tidak disimpan jadi tidak ada van yang disimpan di memori atau disimpan di disk. Catatan penting, setiap kali Anda menggunakan CTE, kueri dijalankan lagi.
Rob
2
Secara pribadi saya belum pernah melihat CTE bekerja lebih baik daripada tabel Temp untuk kecepatan. Dan debugging yang baik jauh lebih mudah dengan tabel temp
Mark Monforti
8

Jadi kueri yang ditugaskan untuk saya optimalkan ditulis dengan dua CTE di SQL server. Itu memakan waktu 28 detik.

Saya menghabiskan dua menit mengubahnya menjadi tabel temp dan kueri membutuhkan waktu 3 detik

Saya menambahkan indeks ke tabel temp di bidang tempat itu bergabung dan menurunkannya menjadi 2 detik

Tiga menit kerja dan sekarang berjalan 12x lebih cepat dengan menghapus CTE. Saya pribadi tidak akan menggunakan CTE karena lebih sulit untuk di-debug juga.

Hal gilanya adalah CTE hanya digunakan sekali dan masih memberikan indeks pada mereka terbukti 50% lebih cepat.

Mark Monforti
sumber
7

CTE tidak akan mengambil ruang fisik apa pun. Ini hanya kumpulan hasil yang bisa kita gunakan bergabung.

Tabel temp sementara. Kita dapat membuat indeks, membatasi seperti tabel normal untuk itu kita perlu mendefinisikan semua variabel.

Ruang lingkup tabel temp hanya dalam sesi. EX: Buka dua jendela kueri SQL

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Jalankan kueri ini di jendela pertama lalu jalankan kueri di bawah ini di jendela kedua Anda dapat menemukan perbedaannya.

select * from #temp
selvaraj
sumber
5
>> "ini hanya kumpulan hasil yang bisa kita gunakan untuk bergabung." -> Ini tidak akurat. CTE bukanlah "kumpulan hasil" tetapi kode sebaris. Mesin kueri SQL Server mengurai kode CTE sebagai bagian dari teks kueri dan membuat rencana eksekusi yang sesuai. Gagasan bahwa CTE sebaris adalah keuntungan besar menggunakan CTE, karena memungkinkan server untuk membuat "rencana eksekusi gabungan"
Ronen Ariely
5

Saya telah menggunakan keduanya tetapi dalam prosedur kompleks besar selalu menemukan tabel temp lebih baik untuk bekerja dengan dan lebih metodis. CTE ada kegunaannya tetapi umumnya dengan data kecil.

Misalnya saya telah membuat sprocs yang kembali dengan hasil penghitungan besar dalam 15 detik namun mengonversi kode ini untuk berjalan dalam CTE dan telah melihatnya berjalan lebih dari 8 menit untuk mencapai hasil yang sama.

Andy_RC
sumber
Ya, saya suka komentar ini. Tampaknya ada paradigma aneh bahwa jika saya dapat menulis sesuatu dengan satu baris kode, bukan dua, saya harus. Saya sedang men-debug sesuatu sekarang yang memiliki 13 CTE bersarang di dalamnya dan CTE disebut data1-data13. Kegilaan total.
Mark Monforti
4

Terlambat ke pesta, tapi ...

Lingkungan tempat saya bekerja sangat dibatasi, mendukung beberapa produk vendor dan menyediakan layanan "nilai tambah" seperti pelaporan. Karena batasan kebijakan dan kontrak, saya biasanya tidak diperbolehkan memiliki tabel terpisah / ruang data dan / atau kemampuan untuk membuat kode permanen [ini menjadi sedikit lebih baik, tergantung pada aplikasinya].

IOW, saya biasanya tidak dapat mengembangkan prosedur yang tersimpan atau UDFs atau tabel temp, dll. Saya cukup banyak harus melakukan semuanya melalui antarmuka aplikasi MY (Crystal Reports - tambahkan / tautkan tabel, setel klausa mana dari w / di CR, dll. ). Satu anugrah KECIL adalah Crystal memungkinkan saya untuk menggunakan PERINTAH (serta SQL Expressions). Beberapa hal yang tidak efisien melalui kemampuan tabel tambahkan / tautan biasa dapat dilakukan dengan menentukan Perintah SQL. Saya menggunakan CTE melalui itu dan mendapatkan hasil yang sangat bagus "dari jarak jauh". CTE juga membantu dengan melaporkan pemeliharaan, tidak memerlukan kode tersebut dikembangkan, diserahkan ke DBA untuk mengkompilasi, mengenkripsi, mentransfer, menginstal, dan kemudian memerlukan pengujian beberapa tingkat. Saya dapat melakukan CTE melalui antarmuka lokal.

Sisi bawah penggunaan CTE w / CR adalah, setiap laporan terpisah. Setiap CTE harus dipertahankan untuk setiap laporan. Di mana saya dapat melakukan SPs dan UDFs, saya dapat mengembangkan sesuatu yang dapat digunakan oleh banyak laporan, hanya memerlukan penautan ke SP dan melewati parameter seolah-olah Anda sedang mengerjakan tabel biasa. CR tidak terlalu pandai menangani parameter ke dalam Perintah SQL, sehingga aspek CR / CTE bisa kurang. Dalam kasus tersebut, saya biasanya mencoba untuk menentukan CTE untuk mengembalikan data yang cukup (tetapi tidak SEMUA data), dan kemudian menggunakan kemampuan pemilihan catatan di CR untuk memotong dan memotong itu.

Jadi ... pilihan saya adalah untuk CTE (sampai saya mendapatkan ruang data saya).

Marc
sumber
4

Satu kegunaan di mana saya menemukan kinerja CTE yang luar biasa bijaksana adalah ketika saya perlu menggabungkan Query yang relatif kompleks ke beberapa tabel yang masing-masing memiliki beberapa juta baris.

Saya menggunakan CTE untuk terlebih dahulu memilih subset berdasarkan kolom yang diindeks untuk pertama-tama memotong tabel ini menjadi beberapa ribu baris yang relevan masing-masing dan kemudian bergabung dengan CTE ke kueri utama saya. Ini secara eksponensial mengurangi waktu proses kueri saya.

Sementara hasil untuk CTE tidak di-cache dan variabel tabel mungkin merupakan pilihan yang lebih baik, saya sebenarnya hanya ingin mencobanya dan menemukan kesesuaian dengan skenario di atas.

pembelian
sumber
Juga, saya pikir karena saya hanya menggunakan CTE dalam bergabung, saya hanya benar-benar mengeksekusi CTE sekali dalam kueri saya sehingga hasil cache tidak menjadi masalah besar dalam hal ini
beli
2

Ini adalah pertanyaan yang benar-benar terbuka, dan itu semua tergantung pada bagaimana digunakan dan jenis tabel temp (variabel Tabel atau tabel tradisional).

Tabel temp tradisional menyimpan data dalam DB temp, yang memperlambat tabel temp; namun variabel tabel tidak.

JoshBerke
sumber
2

Saya baru saja menguji ini - baik CTE dan non-CTE (di mana kueri diketik untuk setiap contoh serikat pekerja) keduanya membutuhkan waktu ~ 31 detik. CTE membuat kode jauh lebih mudah dibaca - potong dari 241 menjadi 130 baris yang sangat bagus. Tabel suhu di sisi lain memotongnya menjadi 132 baris, dan mengambil LIMA DETIK untuk dijalankan. Tidak bercanda. semua pengujian ini disimpan dalam cache- semua kueri dijalankan beberapa kali sebelumnya.

pengguna2989981
sumber
2

Dari pengalaman saya di SQL Server, saya menemukan salah satu skenario di mana CTE mengungguli tabel Temp

Saya perlu menggunakan Kumpulan Data (~ 100000) dari Query kompleks hanya SEKALI dalam Prosedur saya yang tersimpan.

  • Tabel temp menyebabkan overhead pada SQL di mana Prosedur saya berjalan lambat (karena Tabel Temp adalah tabel terwujud nyata yang ada di tempdb dan Bertahan selama masa pakai prosedur saya saat ini)

  • Di sisi lain, dengan CTE, CTE hanya bertahan hingga kueri berikut ini dijalankan. Jadi, CTE adalah struktur dalam memori yang praktis dengan Cakupan terbatas. CTE tidak menggunakan tempdb secara default.

Ini adalah salah satu skenario di mana CTE benar-benar dapat membantu menyederhanakan kode Anda dan Tabel Suhu Mengungguli. Saya telah Menggunakan 2 CTE, kira-kira

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO
Amardeep Kohli
sumber
1
Jawaban Anda tampaknya sangat umum ... Bagaimana Anda mengukur bahwa "CTE mengungguli tabel Temp"? Apakah Anda punya waktu untuk mengukur? Menurut pendapat saya, Anda harus mengedit jawaban Anda dan menambahkan lebih banyak detail.
Il Vic
Ya, saya memiliki pengukuran waktu dan rencana Eksekusi untuk mendukung pernyataan saya.
Amardeep Kohli
Tidak dapat menambahkan img untuk rencana eksekusi karena hak terbatas. Akan memperbarui detail setelah diselesaikan
Amardeep Kohli