Perbedaan antara CTE dan SubQuery?

143

Dari pos ini Bagaimana cara menggunakan ROW_NUMBER dalam prosedur berikut?

Ada dua versi jawaban di mana satu menggunakan a sub-querydan yang lainnya menggunakan a CTEuntuk menyelesaikan masalah yang sama.

Nah, apa keuntungan menggunakan CTE (Common Table Expression)lebih dari 'sub-permintaan` (dengan demikian, lebih mudah dibaca apa yang sebenarnya dilakukan permintaan)

Satu-satunya keuntungan dari menggunakan CTElebih sub-selectadalah bahwa saya benar-benar dapat nama itu sub-query. Apakah ada perbedaan lain antara keduanya ketika CTE digunakan sebagai CTE sederhana (non-rekursif)?

dance2die
sumber
Pertanyaan derivatif dengan diskusi yang baik: stackoverflow.com/q/11169550/781695
pengguna
7
IMO, siapa pun yang berpikir CTE kurang dapat dibaca bahwa gumpalan raksasa dari subqueries yang terjalin belum melihat tumpukan sampah dari pertanyaan berbentuk gigi yang membingungkan yang digunakan di sebagian besar sistem manajemen data perusahaan. Kueri besar dan non-sepele biasanya secara dramatis lebih mudah dibaca nanti atau dengan mata baru daripada subkueri, dan setidaknya dalam kasus Postgres secara ajaib berperforma jauh lebih baik dalam banyak kasus. ([Untuk alasan saya belum mengerti [( stackoverflow.com/questions/33731068/… ), karena sebaliknya sepertinya lebih mungkin.)
zxq9

Jawaban:

102

Dalam versi CTE sub-kueri vs sederhana (non-rekursif), mereka mungkin sangat mirip. Anda harus menggunakan profiler dan rencana eksekusi aktual untuk menemukan perbedaan, dan itu akan spesifik untuk pengaturan Anda (jadi kami tidak dapat memberi tahu Anda jawabannya secara penuh).

Secara umum ; CTE dapat digunakan secara rekursif; sub-kueri tidak bisa. Ini membuatnya sangat cocok untuk struktur pohon.

Marc Gravell
sumber
1
Maaf, saya seharusnya lebih jelas dalam pertanyaan saya. Apa perbedaan antara CTE dan Subquery dalam konteks di mana CTE digunakan SEPERTI subquery?
dance2die
2
@Marc Gravell: Kita dapat melakukan lebih dari itu, karena perilaku profiler tidak dijamin, vs perilaku CTE, yaitu (dalam hal evaluasi).
casperOne
1
Tidak yakin seberapa banyak pernyataan ini masuk akal untuk orang yang melihat CTS dan perbedaan subquery - A CTE can be used recursively; a sub-query cannot. Sebuah contoh akan sangat bagus.
Aniket Thakur
88

Keuntungan utama dari Common Table Expression (ketika tidak menggunakannya untuk permintaan rekursif ) adalah enkapsulasi, alih-alih harus mendeklarasikan sub-kueri di setiap tempat yang ingin Anda gunakan, Anda dapat mendefinisikannya sekali, tetapi memiliki banyak referensi untuk itu.

Namun, ini tidak berarti bahwa itu dijalankan hanya sekali (sesuai per iterasi sebelumnya dari jawaban ini , terima kasih untuk semua yang telah berkomentar). Permintaan pasti berpotensi dieksekusi beberapa kali jika direferensikan beberapa kali; pengoptimal permintaan akhirnya membuat keputusan tentang bagaimana CTE harus ditafsirkan.

casperOne
sumber
"Pikirkan CTE sebagai variabel tabel temp", apakah itu berarti CTE disimpan dalam disk atau dalam memori?
dance2die
Anda tidak dapat menggunakan CTE atau subquery di beberapa kueri, menurut definisi. Saya cukup yakin bahwa pengoptimal menangani subquery dengan cara yang sama itu akan menangani CTE (mengevaluasi hasil yang ditetapkan hanya sekali, terlepas dari berapa kali itu digunakan dalam permintaan 1)
AlexCuse
@AlexCuse: Saya pikir saya sudah cukup mengklarifikasi konteks CTE, tetapi saya menambahkan lebih banyak untuk mencoba dan mengklarifikasi lebih banyak.
casperOne
@AlexCuse: Juga tidak ada implikasi bahwa CTE atau subquery dapat digunakan di banyak tempat. Perbedaan antara CTE dan pengoptimal adalah bahwa perilaku CTE dijamin, sedangkan perilaku pengoptimal tidak.
casperOne
dan saya akan mengakui bahwa mungkin ada beberapa kasus tepi di mana pengoptimal tersedak dan subquery dievaluasi lebih dari sekali, saya belum mengalami apapun. Kemudian lagi, saya menggunakan CTE di mana pun saya bisa;)
AlexCuse
15

CTEYang paling berguna untuk rekursi:

WITH hier(cnt) AS (
        SELECT  1
        UNION ALL
        SELECT  cnt + 1
        FROM    hier
        WHERE   cnt < @n
        )
SELECT  cnt
FROM    hier

akan mengembalikan @nbaris (hingga 101). Berguna untuk kalender, rowset dummy dll.

Mereka juga lebih mudah dibaca (menurut saya).

Terlepas dari ini, CTEdan subqueriesidentik.

Quassnoi
sumber
Dalam MSSQL, Anda perlu menambahkan tanda titik koma (;) sebelum DENGAN, agar bijaksana Anda akan mendapatkan kesalahan. seharusnya;WITH blabla AS ...)
Obinna Nnenanya
2
@ObinnaNnenanya: hanya jika itu bukan pernyataan pertama dalam batch. Mengakhiri pernyataan Anda dengan titik koma adalah ide yang bagus, meskipun SQL Server tidak memberlakukannya di versi saat ini selain sebelumnya WITH, MERGEdan serupa
Quassnoi
10

Satu perbedaan yang belum disebutkan adalah CTE tunggal dapat dirujuk di beberapa bagian serikat

pengguna340140
sumber
8

Kecuali jika saya melewatkan sesuatu, Anda dapat memberi nama CTE dan subquery dengan mudah.

Saya kira perbedaan utamanya adalah keterbacaan (saya menemukan CTE lebih mudah dibaca karena mendefinisikan subquery Anda di depan daripada di tengah).

Dan jika Anda perlu melakukan sesuatu dengan rekursi, Anda akan mengalami sedikit kesulitan melakukan itu dengan subquery;)

AlexCuse
sumber
1
Saya tidak yakin ada perbedaan non-estetika (meskipun saya berharap bahwa dalam situasi tertentu mungkin ada sedikit perbedaan dalam rencana eksekusi). Mau mencerahkanku?
AlexCuse
2
Anda bisa memberi nama CTE, tetapi Anda hanya bisa alias subqueries. Perbedaannya adalah, Anda dapat menggunakan kembali CTE dengan banyak alias (lih. Contoh @Michael Petito dalam komentarnya pada casperOne). Saya tidak tahu cara untuk melakukan itu dengan subkueri.
kmote
7

Satu fakta penting yang belum pernah disebutkan oleh siapa pun adalah bahwa (setidaknya dalam postgres), CTE adalah pagar pengoptimalan:

https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

Artinya, mereka akan diperlakukan sebagai permintaan atom mereka sendiri, daripada dilipat ke dalam rencana kueri keseluruhan. Saya kurang memiliki keahlian untuk memberikan penjelasan yang lebih baik, tetapi Anda harus memeriksa semantik untuk versi sql yang Anda gunakan; untuk pengguna tingkat lanjut, dapat membuat pagar pengoptimalan dapat membantu kinerja jika Anda ahli dalam mengendalikan perencana kueri; dalam 99% kasus, Anda harus menghindari mencoba memberi tahu perencana kueri apa yang harus dilakukan, karena apa yang menurut Anda akan lebih cepat cenderung lebih buruk daripada apa yang menurutnya akan lebih cepat. :-)

Ajax
sumber
6

Menambahkan ke jawaban orang lain, jika Anda memiliki satu dan subquery yang sama digunakan beberapa kali, Anda dapat mengganti semua subqueries ini dengan satu CTE. Ini memungkinkan Anda menggunakan kembali kode Anda dengan lebih baik.

AK
sumber
4

Satu hal yang perlu Anda pahami juga adalah bahwa dalam versi SQL Server yang lebih lama (ya banyak orang masih perlu mendukung database SQL Server 2000), CTE tidak diperbolehkan dan kemudian tabel turunannya adalah solusi terbaik Anda.

HLGEM
sumber
2

PETUNJUK: (MAXRECURSION n)

Anda dapat membatasi jumlah level rekursi yang diizinkan untuk pernyataan tertentu dengan menggunakan MAXRECURSIONpetunjuk dan nilai antara 0 dan 32.767 dalam OPTIONklausa

Misalnya, Anda dapat mencoba:

OPTION 
      (MAXRECURSION 150)

GO
Dasar_
sumber