Manfaat Common Table Expression (CTE)?

21

Dari msdn :

Tidak seperti tabel turunan, CTE bisa menjadi rujukan sendiri dan dapat direferensikan beberapa kali dalam kueri yang sama.

Saya menggunakan CTE cukup banyak, tetapi saya tidak pernah berpikir mendalam tentang manfaat menggunakannya.

Jika saya mereferensikan CTE beberapa kali dalam permintaan yang sama:

  • Apakah ada manfaat kinerja?
  • Jika saya melakukan self join, apakah SQL Server akan memindai tabel target dua kali?
Royi Namir
sumber
2
Profiler harus memberi tahu Anda jika memindai dua kali. IMHO, CTE luar biasa untuk rekursi.
Dan Andrews
3
Tidak ada jawaban sulit ketika pengoptimal kueri sedang dimainkan. Beberapa permintaan akan melihat manfaat kinerja, beberapa tidak. Kadang-kadang menggunakan tabel temp bukan CTE akan lebih cepat, kadang-kadang tidak.

Jawaban:

25

Sebagai aturan, CTE tidak akan pernah meningkatkan kinerja .

CTE pada dasarnya adalah pandangan sekali pakai. Tidak ada statistik tambahan yang disimpan, tidak ada indeks, dll. Ini berfungsi sebagai singkatan untuk subquery.

Menurut pendapat saya mereka dapat digunakan dengan mudah MUDAH (saya melihat banyak kode yang terlalu sering digunakan dalam pekerjaan saya). Beberapa jawaban yang baik ada di sini, tetapi jika Anda perlu merujuk ke sesuatu lebih dari sekali, atau lebih dari beberapa ratus ribu baris, masukkan ke dalam #temptabel dan gantilah.

JNK
sumber
3
Setuju. Kecuali untuk CTE rekursif, maka mereka hanya membantu keterbacaan
gbn
Bagaimana jika CTE mengembalikan hanya beberapa baris (sehingga dapat disimpan dalam memori) yang mahal untuk dihitung (agregasi pada tabel besar) dan hasilnya digunakan lebih dari satu kali? Itu harus meningkatkan kinerja, bukan? (setidaknya itulah pengalaman saya dengan PostgreSQL dan Oracle di mana tabel temp digunakan sangat jarang)
a_horse_with_no_name
2
@a_horse_with_no_name - itu akan sama dengan menjadikannya subquery. Jika hasilnya digunakan lebih dari sekali dalam satu permintaan, itu akan digunakan kembali dan tidak dihitung ulang. Jika digunakan di lebih dari satu permintaan, maka a CTEadalah pilihan yang buruk karena hasilnya dibuang setelah permintaan pertama.
JNK
@JNK: terima kasih. Tampaknya SQL Server berperilaku berbeda di sini.
a_horse_with_no_name
Beberapa orang menemukan CTE lebih mudah dibaca dalam keadaan tertentu FWIW stackoverflow.com/a/11170918/32453
rogerdpack
14

Satu tempat selain rekursi di mana saya menemukan CTE sangat berguna adalah ketika membuat kueri pelaporan yang kompleks. Saya menggunakan serangkaian CTE untuk mendapatkan potongan data yang saya butuhkan dan kemudian menggabungkannya di pilih terakhir. Saya menemukan mereka lebih mudah untuk mempertahankan daripada melakukan hal yang sama dengan banyak tabel turunan atau 20 bergabung dan saya menemukan bahwa saya dapat lebih yakin bahwa itu mengembalikan data yang benar tanpa efek dari beberapa catatan karena satu-banyak hubungan di semua bergabung berbeda. Biarkan saya memberi contoh cepat:

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

Jadi dengan memisahkan potongan informasi yang berbeda yang Anda inginkan, Anda dapat memeriksa setiap bagian secara terpisah (menggunakan pilihan yang dikomentari, dengan membatalkan komentar masing-masing secara individual dan hanya berjalan sejauh yang dipilih) dan jika Anda perlu melakukan perubahan pada pengeluaran perhitungan (dalam contoh ini), lebih mudah ditemukan daripada ketika mereka dicampur bersama menjadi satu permintaan besar. Tentu saja pertanyaan pelaporan aktual yang saya gunakan untuk ini pada umumnya jauh lebih rumit daripada contohnya.

HLGEM
sumber
1
Hanya untuk melaporkan kueri? Sistem saya bekerja pada setiap hari memiliki permintaan transaksi yang rumit. Anehnya, pertanyaan pelaporan kami sering kali merupakan pertanyaan sederhana kami. (Mengecualikan pertanyaan CRUD sepele joinless tentu saja).
Kevin Cathcart
Saya menggunakan itu sebagai contoh karena itu biasanya yang paling rumit di sini
HLGEM
+1 terkadang kueri yang lebih logis (dapat dibaca manusia) lebih disukai daripada kueri yang berpotensi berkinerja lebih baik.
onedaywhen
Iya nih. Mengingat bahwa CTE biasanya akan menghasilkan rencana hasil yang sama, saya tidak melihat alasan untuk membuat monstrositas bersarang-banyak yang mengerikan bersarang - ketika kita bisa meletakkan setiap komponen secara visual dalam urutan yang mereka butuhkan. Saya mengimpor file XML dan melakukan berbagai akrobat untuk mendapatkan data ke formulir yang tepat, yang tidak tertahankan untuk ditulis / dibaca tanpa CTE. (Beberapa kode lama saya mungkin memiliki subqueries yang mengerikan semuanya!)
underscore_d
0

Seperti biasa itu tergantung tetapi ada kasus di mana kinerja sangat meningkat. Saya melihatnya dengan pernyataan INSERT INTO SELECT di mana Anda menggunakan CTE untuk pilih dan kemudian menggunakannya di INSERT INTO. Ini mungkin ada hubungannya dengan RCSI yang diaktifkan untuk database tetapi untuk saat-saat ketika sangat sedikit yang dipilih dapat membantu sedikit.

Ron S
sumber