Penelusuran Parameter
Parameterisasi kueri mendorong penggunaan kembali rencana eksekusi yang di-cache, sehingga menghindari kompilasi yang tidak perlu, dan mengurangi jumlah kueri ad-hoc dalam cache rencana.
Ini semua adalah hal yang baik, asalkan kueri yang diparameterisasi benar-benar harus menggunakan rencana eksekusi cache yang sama untuk nilai parameter yang berbeda. Rencana eksekusi yang efisien untuk satu nilai parameter mungkin tidak menjadi pilihan yang baik untuk kemungkinan nilai parameter lainnya.
Ketika parameter sniffing diaktifkan (default), SQL Server memilih rencana eksekusi berdasarkan nilai parameter tertentu yang ada pada waktu kompilasi. Asumsi implisit adalah bahwa pernyataan berparameter paling sering dieksekusi dengan nilai parameter yang paling umum. Ini terdengar cukup masuk akal (bahkan jelas) dan memang sering berhasil.
Masalah dapat terjadi ketika kompilasi ulang otomatis dari rencana cache terjadi. Kompilasi ulang dapat dipicu karena berbagai alasan, misalnya karena indeks yang digunakan oleh paket yang di-cache telah dihapus (kebenaran kompilasi ulang) atau karena informasi statistik telah berubah (optimal kompilasi ulang).
Apapun penyebabnya yang tepat dari kompilasi ulang rencana, ada kemungkinan bahwa tidak biasa nilai sedang diteruskan sebagai parameter pada saat rencana baru dibuat. Hal ini dapat mengakibatkan rencana cache baru (berdasarkan nilai parameter atipikal yang diendus) yang tidak baik untuk sebagian besar eksekusi yang akan digunakan kembali.
Tidak mudah untuk memprediksi kapan rencana eksekusi tertentu akan dikompilasi ulang (misalnya, karena statistik telah cukup berubah) yang mengakibatkan situasi di mana rencana yang dapat digunakan kembali berkualitas baik dapat tiba-tiba digantikan oleh rencana yang sangat berbeda yang dioptimalkan untuk nilai parameter atipikal.
Salah satu skenario tersebut terjadi ketika nilai atipikal sangat selektif, menghasilkan rencana yang dioptimalkan untuk sejumlah kecil baris. Rencana tersebut akan sering menggunakan eksekusi single-threaded, loop bersarang bergabung, dan pencarian. Masalah kinerja yang serius dapat muncul saat paket ini digunakan kembali untuk nilai parameter berbeda yang menghasilkan jumlah baris yang jauh lebih banyak.
Menonaktifkan Pengendapan Parameter
Pengendapan parameter dapat dinonaktifkan menggunakan tanda pelacakan terdokumentasi 4136. Tanda pelacakan juga didukung untuk per-kueri gunakan melalui QUERYTRACEON
petunjuk kueri. Keduanya berlaku dari SQL Server 2005 Service Pack 4 dan seterusnya (dan sedikit lebih awal jika Anda menerapkan pembaruan kumulatif ke Service Pack 3).
Dimulai dengan SQL Server 2016, sniffing parameter juga dapat dinonaktifkan di tingkat basis data , menggunakan PARAMETER_SNIFFING
argumen ke ALTER DATABASE SCOPED CONFIGURATION
.
Saat parameter sniffing dinonaktifkan, SQL Server menggunakan distribusi rata-rata statistik untuk memilih rencana eksekusi.
Ini juga terdengar seperti pendekatan yang masuk akal (dan dapat membantu menghindari situasi di mana rencana dioptimalkan untuk nilai parameter selektif yang tidak biasa), tetapi ini juga bukan strategi yang sempurna:Rencana yang dioptimalkan untuk nilai 'rata-rata' mungkin akan berakhir menjadi benar-benar kurang optimal untuk nilai parameter yang umum terlihat.
Pertimbangkan rencana eksekusi yang berisi operator yang menghabiskan memori seperti sortir dan hash. Karena memori dicadangkan sebelum eksekusi kueri dimulai, rencana berparameter berdasarkan nilai distribusi rata-rata dapat tumpah ke tempdb untuk nilai parameter umum yang menghasilkan lebih banyak data daripada yang diharapkan pengoptimal.
Reservasi memori biasanya tidak dapat bertambah selama eksekusi kueri, terlepas dari berapa banyak memori bebas yang mungkin dimiliki server. Aplikasi tertentu memang mendapat manfaat dari mematikan parameter sniffing (lihat posting arsip ini oleh Tim Kinerja Dynamics AX sebagai contoh).
Untuk sebagian besar beban kerja, menonaktifkan sniffing parameter sepenuhnya adalah solusi yang salah , dan bahkan bisa menjadi bencana. Parameter sniffing adalah pengoptimalan heuristik:Ini bekerja lebih baik daripada menggunakan nilai rata-rata di sebagian besar sistem, hampir sepanjang waktu.
Petunjuk Kueri
SQL Server menyediakan berbagai petunjuk kueri dan opsi lain untuk menyesuaikan perilaku parameter sniffing:
OPTIMIZE FOR (@parameter = value)
petunjuk kueri membuat rencana yang dapat digunakan kembali berdasarkan nilai tertentu.OPTIMIZE FOR (@parameter UNKNOWN)
menggunakan statistik distribusi rata-rata untuk parameter tertentu.OPTIMIZE FOR UNKNOWN
menggunakan distribusi rata-rata untuk semua parameter (efek yang sama seperti tanda jejak 4136).WITH RECOMPILE
opsi prosedur tersimpan mengkompilasi rencana prosedur baru untuk setiap eksekusi.OPTION (RECOMPILE)
petunjuk kueri mengkompilasi rencana baru untuk pernyataan individual.
Teknik lama “penyembunyian parameter” (menetapkan parameter prosedur ke variabel lokal, dan mereferensikan variabel sebagai gantinya) memiliki efek yang sama seperti menentukan OPTIMIZE FOR UNKNOWN
. Ini dapat berguna pada contoh sebelum SQL Server 2008 (OPTIMIZE FOR
petunjuk baru untuk tahun 2008).
Dapat dikatakan bahwa setiap pernyataan berparameter harus diperiksa sensitivitasnya terhadap nilai parameter, dan dibiarkan saja (jika perilaku default berfungsi dengan baik) atau secara eksplisit diisyaratkan menggunakan salah satu opsi di atas.
Hal ini jarang dilakukan dalam praktik, sebagian karena melakukan analisis komprehensif untuk semua nilai parameter yang mungkin memakan waktu lama dan memerlukan keterampilan yang cukup maju.
Paling sering, analisis semacam itu tidak dilakukan dan masalah sensitivitas parameter ditangani sebagai dan ketika mereka terjadi dalam produksi.
Kurangnya analisis sebelumnya mungkin merupakan salah satu alasan utama mengapa parameter sniffing memiliki reputasi yang buruk. Penting untuk menyadari potensi masalah yang muncul, dan untuk melakukan setidaknya analisis cepat pada pernyataan yang mungkin menyebabkan masalah kinerja saat dikompilasi ulang dengan nilai parameter yang tidak biasa.
Apa itu parameter?
Beberapa orang akan mengatakan bahwa SELECT
pernyataan yang mereferensikan variabel lokal adalah “pernyataan berparameter” semacam itu, tapi itu bukan definisi yang digunakan SQL Server.
Indikasi yang masuk akal bahwa pernyataan menggunakan parameter dapat ditemukan dengan melihat properti rencana (lihat Parameter tab di Sentry One Plan Explorer. Atau klik node root rencana kueri di SSMS, buka Properties jendela, dan perluas Daftar Parameter simpul):
'Nilai yang dikompilasi' menunjukkan nilai parameter yang diendus yang digunakan untuk mengkompilasi rencana yang di-cache. 'Nilai runtime' menunjukkan nilai parameter pada eksekusi tertentu yang ditangkap dalam rencana.
Salah satu dari properti ini mungkin kosong atau hilang dalam keadaan yang berbeda. Jika kueri tidak diparameterisasi, semua properti akan hilang.
Hanya karena tidak ada yang sederhana di SQL Server, ada situasi di mana daftar parameter dapat diisi, tetapi pernyataan itu masih belum diparameterisasi. Ini dapat terjadi ketika SQL Server mencoba parameterisasi sederhana (dibahas nanti) tetapi memutuskan bahwa upaya tersebut "tidak aman". Dalam hal ini, penanda parameter akan ada, tetapi rencana eksekusi sebenarnya tidak diparameterisasi.
Mengendus bukan hanya untuk Stored Procedures
Pengendapan parameter juga terjadi ketika kumpulan secara eksplisit diparameterisasi untuk digunakan kembali menggunakan sp_executesql
.
Misalnya:
JALANKAN sys.sp_executesql N' SELECT P.ProductID, P.Name, TotalQty =SUM(TH.Quantity) FROM Production.Product AS P GABUNG Production.TransactionHistory AS TH PADA TH.ProductID =P.ProductID WHERE P.Name LIKE @NameLike GROUP OLEH P.ProductID, P.Name; ', N'@NameLike nvarchar(50)', @NameLike =N'K%';
Pengoptimal memilih rencana eksekusi berdasarkan nilai terendus dari @NameLike
parameter. Nilai parameter “K%” diperkirakan cocok dengan sangat sedikit baris di Product
tabel, sehingga pengoptimal memilih gabungan loop bersarang dan strategi pencarian kunci:
Mengeksekusi pernyataan lagi dengan nilai parameter “[H-R]%” (yang akan cocok dengan lebih banyak baris) menggunakan kembali paket berparameter yang di-cache:
JALANKAN sys.sp_executesql N' SELECT P.ProductID, P.Name, TotalQty =SUM(TH.Quantity) FROM Production.Product AS P GABUNG Production.TransactionHistory AS TH PADA TH.ProductID =P.ProductID WHERE P.Name LIKE @NameLike GROUP OLEH P.ProductID, P.Name; ', N'@NameLike nvarchar(50)', @NameLike =N'[H-R]%';
PetualanganKarya database sampel terlalu kecil untuk membuat ini menjadi bencana kinerja, tetapi rencana ini tentu saja tidak optimal untuk nilai parameter kedua.
Kita dapat melihat paket yang akan dipilih oleh pengoptimal dengan mengosongkan cache paket dan menjalankan kueri kedua lagi:
Dengan perkiraan jumlah kecocokan yang lebih besar, pengoptimal menentukan bahwa gabungan hash dan agregasi hash adalah strategi yang lebih baik.
Fungsi T-SQL
Pengendapan parameter juga terjadi dengan fungsi T-SQL, meskipun cara rencana eksekusi dibuat dapat membuat hal ini lebih sulit untuk dilihat.
Ada alasan bagus untuk menghindari fungsi skalar dan multi-pernyataan T-SQL secara umum, jadi hanya untuk tujuan pendidikan, berikut adalah versi fungsi bernilai tabel multi-pernyataan T-SQL dari kueri pengujian kami:
CREATE FUNCTION dbo.F (@NameLike nvarchar(50))RETURNS @Result TABLE( ProductID integer NOT NULL PRIMARY KEY, Nama nvarchar(50) NOT NULL, TotalQty integer NOT NULL)DENGAN SCHEMABINDINGASBEGIN INSERT @Result SELECT P.ProductID , P.Name, TotalQty =SUM(TH.Quantity) FROM Production.Product AS P GABUNG Production.TransactionHistory AS TH ON TH.ProductID =P.ProductID WHERE P.Name LIKE @NameLike GROUP OLEH P.ProductID, P.Name; RETURN;END;
Kueri berikut menggunakan fungsi untuk menampilkan informasi nama produk yang dimulai dengan 'K':
SELECT Result.ProductID, Result.Name, Result.TotalQtyFROM dbo.F(N'K%') AS Result;
Melihat parameter sniffing dengan fungsi yang disematkan lebih sulit karena SQL Server tidak mengembalikan rencana kueri pasca-eksekusi (aktual) terpisah untuk setiap pemanggilan fungsi. Fungsi tersebut dapat dipanggil berkali-kali dalam satu pernyataan, dan pengguna tidak akan terkesan jika SSMS mencoba menampilkan sejuta rencana panggilan fungsi untuk satu kueri.
Sebagai hasil dari keputusan desain ini, rencana aktual yang dikembalikan oleh SQL Server untuk kueri pengujian kami tidak terlalu membantu:
Namun demikian, ada ada cara untuk melihat parameter sniffing beraksi dengan fungsi yang disematkan. Metode yang saya pilih untuk digunakan di sini adalah memeriksa cache paket:
Hasil ini menunjukkan bahwa rencana fungsi telah dieksekusi sekali, dengan biaya 201 pembacaan logis dengan waktu berlalu 2891 mikrodetik, dan eksekusi terbaru mengembalikan satu baris. Representasi paket XML yang dikembalikan menunjukkan bahwa nilai parameter adalah mengendus:
Sekarang jalankan pernyataan lagi, dengan parameter yang berbeda:
SELECT Result.ProductID, Result.Name, Result.TotalQtyFROM dbo.F(N'[H-R]%') SEBAGAI Hasil;
Rencana pasca-eksekusi menunjukkan bahwa 306 baris dikembalikan oleh fungsi:
Kueri cache rencana menunjukkan rencana eksekusi yang di-cache untuk fungsi yang telah digunakan kembali (execution_count
=2):
Ini juga menunjukkan jumlah pembacaan logis yang jauh lebih tinggi, dan waktu berlalu yang lebih lama dibandingkan dengan proses sebelumnya. Ini konsisten dengan penggunaan kembali loop bersarang dan rencana pencarian, tetapi untuk memastikan sepenuhnya, rencana fungsi pasca-eksekusi dapat ditangkap menggunakan Acara yang Diperpanjang atau SQL Server Profiler alat:
Karena parameter sniffing berlaku untuk fungsi, modul ini dapat mengalami perubahan tak terduga yang sama dalam kinerja yang umumnya terkait dengan prosedur tersimpan.
Misalnya, pertama kali suatu fungsi direferensikan, rencana mungkin di-cache yang tidak menggunakan paralelisme. Eksekusi berikutnya dengan nilai parameter yang akan mendapat manfaat dari paralelisme (tetapi menggunakan kembali paket serial yang di-cache) akan menunjukkan kinerja yang buruk secara tidak terduga.
Masalah ini bisa sulit untuk diidentifikasi karena SQL Server tidak mengembalikan rencana pasca-eksekusi terpisah untuk panggilan fungsi seperti yang telah kita lihat. Menggunakan Acara yang Diperpanjang atau Profiler untuk secara rutin menangkap rencana pasca-eksekusi bisa sangat intensif sumber daya, jadi sering kali masuk akal untuk menggunakan teknik itu dengan cara yang sangat bertarget. Kesulitan seputar masalah sensitivitas parameter fungsi debugging berarti lebih bermanfaat melakukan analisis (dan pengkodean secara defensif) sebelum fungsi mencapai produksi.
Pengendus parameter bekerja dengan cara yang persis sama dengan fungsi skalar yang ditentukan pengguna T-SQL (kecuali in-line, pada SQL Server 2019 dan seterusnya). Fungsi bernilai tabel sebaris tidak menghasilkan rencana eksekusi terpisah untuk setiap pemanggilan, karena (seperti namanya) ini dibariskan ke dalam kueri pemanggilan sebelum kompilasi.
Hati-hati dengan NULL yang Dicuri
Kosongkan cache paket dan minta perkiraan (pra-eksekusi) rencana untuk kueri pengujian:
SELECT Result.ProductID, Result.Name, Result.TotalQtyFROM dbo.F(N'K%') AS Result;
Anda akan melihat dua rencana eksekusi, yang kedua adalah untuk pemanggilan fungsi:
Batasan sniffing parameter dengan fungsi yang disematkan dalam rencana perkiraan berarti nilai parameter diendus sebagai NULL
(bukan “K%”):
Dalam versi SQL Server sebelum 2012, paket ini (dioptimalkan untuk NULL
parameter) di-cache untuk digunakan kembali . Ini sangat disayangkan, karena NULL
tidak mungkin menjadi nilai parameter yang representatif, dan tentu saja bukan nilai yang ditentukan dalam kueri.
SQL Server 2012 (dan yang lebih baru) tidak menyimpan paket yang dihasilkan dari permintaan "rencana perkiraan", meskipun masih akan menampilkan rencana fungsi yang dioptimalkan untuk NULL
nilai parameter pada waktu kompilasi.
Parameterisasi Sederhana dan Terpaksa
Pernyataan T-SQL ad-hoc yang berisi nilai literal konstan dapat diparameterisasi oleh SQL Server, baik karena kueri memenuhi syarat untuk parameterisasi sederhana atau karena opsi database untuk parameterisasi paksa diaktifkan (atau panduan rencana digunakan untuk efek yang sama).
Pernyataan yang diparameterisasi dengan cara ini juga tunduk pada parameter sniffing. Kueri berikut memenuhi syarat untuk parameterisasi sederhana:
PILIH A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 =N'Heidestieg Straße 8664';
Perkiraan rencana eksekusi menunjukkan perkiraan 2,5 baris berdasarkan nilai parameter yang diendus:
Faktanya, kueri mengembalikan 7 baris (estimasi kardinalitas tidak sempurna, bahkan ketika nilai diendus):
Pada titik ini, Anda mungkin bertanya-tanya di mana buktinya bahwa kueri ini diparameterisasi, dan nilai parameter yang dihasilkan diendus. Jalankan kueri untuk kedua kalinya dengan nilai yang berbeda:
SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 =N'Winter der Böck 8550';
Kueri mengembalikan satu baris:
Rencana eksekusi menunjukkan eksekusi kedua menggunakan kembali rencana berparameter yang dikompilasi menggunakan nilai yang diendus:
Parameterisasi dan sniffing adalah aktivitas yang terpisah
Pernyataan ad-hoc dapat diparameterisasi oleh SQL Server tanpa nilai parameter sedang diendus.
Untuk mendemonstrasikan, kita dapat menggunakan flag trace 4136 untuk menonaktifkan parameter sniffing untuk batch yang akan diparameterisasi oleh server:
DBCC FREEPROCCACHE;DBCC TRACEON (4136);GOSELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 =N'Heidestieg Straße 8664';GOSELECT A.AddressLine1, A.City , A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 =N'Winter der Böck 8550';GODBCC TRACEOFF (4136);
Script menghasilkan pernyataan yang diparameterisasi, tetapi nilai parameter tidak diendus untuk tujuan estimasi kardinalitas. Untuk melihat ini, kita dapat memeriksa cache paket:
DENGAN XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')PILIH DECP.cacheobjtype, DECP.objtype, DECP.usecounts, DECP.plan_handle, parameterized_plan_handle =DEQP.query_plan. nilai ( '(//StmtSimple)[1]/@ParameterizedPlanHandle', 'NVARCHAR(100)' )FROM sys.dm_exec_cached_plans AS DECPCROSS BERLAKU sys.dm_exec_sql_text(DECP.plan_handle) SEBAGAI DESTCROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DESTCROSS APPLY DEQPWHERE DEST.[text] LIKE N'%AddressLine1%' AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';
Hasilnya menunjukkan dua entri cache untuk kueri ad-hoc, yang ditautkan ke paket kueri berparameter (disiapkan) oleh pegangan paket berparameter.
Paket parameter digunakan dua kali:
Rencana eksekusi menunjukkan perkiraan kardinalitas yang berbeda sekarang setelah parameter sniffing dinonaktifkan:
Bandingkan estimasi 1.44571 baris dengan estimasi 2,5 baris yang digunakan saat parameter sniffing diaktifkan.
Dengan menonaktifkan sniffing, perkiraan berasal dari informasi frekuensi rata-rata tentang AddressLine1
kolom. Ekstrak DBCC SHOW_STATISTICS
keluaran untuk indeks yang bersangkutan menunjukkan bagaimana angka ini dihitung:Mengalikan jumlah baris dalam tabel (19.614) dengan kepadatan (7.370826e-5) menghasilkan perkiraan baris 1,44571.
Catatan tambahan: Secara umum diyakini bahwa hanya perbandingan bilangan bulat yang menggunakan indeks unik yang dapat memenuhi syarat untuk parameterisasi sederhana. Saya sengaja memilih contoh ini (perbandingan string menggunakan indeks non-unik) untuk membantahnya.
DENGAN RECOMPILE dan OPTION (RECOMPILE)
Ketika masalah sensitivitas parameter ditemukan, saran umum di forum dan situs Tanya Jawab adalah "menggunakan kompilasi ulang" (dengan asumsi opsi penyetelan lain yang disajikan sebelumnya tidak cocok). Sayangnya, saran tersebut sering disalahartikan sebagai menambahkan WITH RECOMPILE
opsi untuk prosedur tersimpan.
Menggunakan WITH RECOMPILE
secara efektif mengembalikan kita ke perilaku SQL Server 2000, di mana seluruh prosedur tersimpan dikompilasi ulang pada setiap eksekusi.
Alternatif yang lebih baik , pada SQL Server 2005 dan yang lebih baru, adalah menggunakan OPTION (RECOMPILE)
petunjuk kueri hanya pada pernyataan yang menderita masalah parameter-sniffing. Petunjuk kueri ini menghasilkan kompilasi ulang pernyataan bermasalah hanya. Rencana eksekusi untuk pernyataan lain dalam prosedur tersimpan di-cache dan digunakan kembali seperti biasa.
Menggunakan WITH RECOMPILE
juga berarti rencana yang dikompilasi untuk prosedur tersimpan tidak di-cache. Akibatnya, tidak ada informasi kinerja yang dipertahankan di DMV seperti sys.dm_exec_query_stats
.
Menggunakan petunjuk kueri sebagai gantinya berarti bahwa rencana yang dikompilasi dapat di-cache, dan informasi kinerja tersedia di DMV (meskipun terbatas pada eksekusi terbaru, hanya untuk pernyataan yang terpengaruh).
Untuk instans yang menjalankan setidaknya SQL Server 2008 build 2746 (Paket Layanan 1 dengan Pembaruan Kumulatif 5), menggunakan OPTION (RECOMPILE)
memiliki keuntungan signifikan lainnya lebih dari WITH RECOMPILE
:Hanya OPTION (RECOMPILE)
mengaktifkan Optimalisasi Penyematan Parameter .
Optimasi Penyematan Parameter
Mengendus nilai parameter memungkinkan pengoptimal menggunakan nilai parameter untuk mendapatkan perkiraan kardinalitas. Keduanya WITH RECOMPILE
dan OPTION (RECOMPILE)
menghasilkan rencana kueri dengan perkiraan yang dihitung dari nilai parameter aktual pada setiap eksekusi.
Pengoptimalan Penyematan Parameter mengambil proses ini selangkah lebih maju. Parameter kueri diganti dengan nilai konstanta literal selama penguraian kueri.
Pengurai mampu melakukan penyederhanaan yang sangat kompleks, dan pengoptimalan kueri selanjutnya dapat menyempurnakan hal-hal lebih jauh. Pertimbangkan prosedur tersimpan berikut, yang menampilkan WITH RECOMPILE
pilihan:
BUAT PROSEDUR dbo.P @NameLike nvarchar(50), @Sort tinyintWITH RECOMPILEASMULAI PILIH TOP (5) ProductID, Nama FROM Production.Product WHERE @NameLike IS NULL ATAU Nama LIKE @NameLike ORDER BY CASE WHEN @Sort =1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort =2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort =3 THEN Name ELSE NULL END ASC, CASE WHEN @Sort =4 THEN Name ELSE NULL END DESC;END;
Prosedur dijalankan dua kali, dengan nilai parameter berikut:
EXECUTE dbo.P @NameLike =N'K%', @Sort =1;GOEXECUTE dbo.P @NameLike =N'[H-R]%', @Sort =4;
Karena WITH RECOMPILE
digunakan, prosedur dikompilasi ulang sepenuhnya pada setiap eksekusi. Nilai parameter diendus setiap kali, dan digunakan oleh pengoptimal untuk menghitung perkiraan kardinalitas.
Rencana untuk eksekusi prosedur pertama benar-benar tepat, memperkirakan 1 baris:
Eksekusi kedua memperkirakan 360 baris, sangat dekat dengan 366 yang terlihat saat dijalankan:
Kedua paket menggunakan strategi eksekusi umum yang sama:Pindai semua baris dalam indeks, dengan menerapkan WHERE
predikat klausa sebagai sisa; menghitung CASE
ekspresi yang digunakan dalam ORDER BY
ayat; dan lakukan Urutan N Teratas pada hasil CASE
ekspresi.
OPSI (REKOMPILASI)
Sekarang buat ulang prosedur tersimpan menggunakan OPTION (RECOMPILE)
petunjuk kueri alih-alih WITH RECOMPILE
:
BUAT PROSEDUR dbo.P @NameLike nvarchar(50), @Sort tinyintASBEGIN PILIH TOP (5) ProductID, Nama FROM Production.Product WHERE @NameLike IS NULL ATAU Nama LIKE @NameLike ORDER BY CASE WHEN @Sort =1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort =2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort =3 THEN Name ELSE NULL END ASC, CASE WHEN @Sort =4 THEN Name ELSE NULL END DESC OPTION (RECOMPILE);END;
Menjalankan prosedur tersimpan dua kali dengan nilai parameter yang sama seperti sebelumnya menghasilkan berbeda secara dramatis rencana eksekusi.
Ini adalah rencana eksekusi pertama (dengan parameter yang meminta nama dimulai dengan “K”, diurutkan berdasarkan ProductID
naik):
Pengurai menyematkan nilai parameter dalam teks kueri, menghasilkan bentuk peralihan berikut:
SELECT TOP (5) ProductID, NameFROM Production.ProductWHERE 'K%' IS NULL ATAU Nama LIKE 'K%'ORDER BY CASE WHEN 1 =1 THE ProductID ELSE NULL END ASC, CASE WHEN 1 =2 THEN ProductID ELSE NULL END DESC, CASE WHEN 1 =3 THEN Nama ELSE NULL END ASC, CASE WHEN 1 =4 THEN Name ELSE NULL END DESC;
Pengurai kemudian melangkah lebih jauh, menghilangkan kontradiksi dan sepenuhnya mengevaluasi CASE
ekspresi. Ini menghasilkan:
SELECT TOP (5) ProductID, NameFROM Production.ProductWHERE Name LIKE 'K%'ORDER BY ProductID ASC, NULL DESC, NULL ASC, NULL DESC;
Anda akan mendapatkan pesan kesalahan jika Anda mencoba mengirimkan kueri itu langsung ke SQL Server, karena memesan dengan nilai konstan tidak diperbolehkan. Namun demikian, ini adalah bentuk yang dihasilkan oleh parser. Ini diizinkan secara internal karena muncul sebagai hasil penerapan pengoptimalan penyematan parameter . Kueri yang disederhanakan membuat hidup lebih mudah bagi pengoptimal kueri:
Pemindaian Indeks Berkelompok menerapkan LIKE
predikat sebagai sisa. Hitung Skalar menyediakan NULL
constant yang konstan nilai-nilai. Atas mengembalikan 5 baris pertama dalam urutan yang diberikan oleh Indeks Tergugus (menghindari semacam). Di dunia yang sempurna, pengoptimal kueri juga akan menghapus Compute Scalar yang mendefinisikan NULLs
, karena tidak digunakan selama eksekusi kueri.
Eksekusi kedua mengikuti proses yang persis sama, menghasilkan rencana kueri (untuk nama yang dimulai dengan huruf “H” hingga “R”, diurutkan berdasarkan Name
turun) seperti ini:
Paket ini menampilkan Pencarian Indeks Nonclustered yang mencakup LIKE
rentang, sisa LIKE
predikat, konstanta NULLs
seperti sebelumnya, dan Top (5). Pengoptimal kueri memilih untuk melakukan BACKWARD
pemindaian jangkauan di Pencarian Indeks untuk sekali lagi menghindari penyortiran.
Bandingkan paket di atas dengan yang dibuat menggunakan WITH RECOMPILE
, yang tidak dapat menggunakan pengoptimalan penyematan parameter :
Contoh demo ini mungkin lebih baik diimplementasikan sebagai rangkaian IF
pernyataan dalam prosedur (satu untuk setiap kombinasi nilai parameter). Ini dapat memberikan manfaat rencana kueri yang serupa, tanpa menimbulkan kompilasi pernyataan setiap kali. Dalam skenario yang lebih kompleks, tingkat pernyataan dikompilasi ulang dengan penyematan parameter yang disediakan oleh OPTION (RECOMPILE)
bisa menjadi teknik pengoptimalan yang sangat berguna.
Pembatasan Penyematan
Ada satu skenario di mana menggunakan OPTION (RECOMPILE)
tidak akan menghasilkan optimasi penyematan parameter yang diterapkan. Jika pernyataan ditetapkan ke variabel, nilai parameter tidak disematkan:
BUAT PROSEDUR dbo.P @NameLike nvarchar(50), @Sort tinyintASBEGIN DECLARE @ProductID integer, @Name nvarchar(50); PILIH ATAS (1) @ProductID =ProductID, @Name =Nama DARI Production.Product WHERE @NameLike IS NULL ATAU Nama LIKE @NameLike ORDER BY CASE WHEN @Sort =1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort =2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort =3 THEN Nama ELSE NULL END ASC, CASE WHEN @Sort =4 THEN Name ELSE NULL END DESC OPTION (RECOMPILE);END;
Karena SELECT
pernyataan sekarang ditetapkan ke variabel, rencana kueri yang dihasilkan sama seperti ketika WITH RECOMPILE
digunakan. Nilai parameter masih diendus dan digunakan oleh pengoptimal kueri untuk estimasi kardinalitas, dan OPTION (RECOMPILE)
masih hanya mengkompilasi pernyataan tunggal, hanya manfaat dari penyematan parameter hilang.