Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Parameterisasi Sederhana dan Rencana Trivial — Bagian 3

Rencana Eksekusi

Ini lebih rumit daripada yang Anda harapkan dari informasi yang diberikan dalam rencana eksekusi jika pernyataan SQL menggunakan parameterisasi sederhana . Tidak mengherankan bahkan pengguna SQL Server yang sangat berpengalaman cenderung salah paham, mengingat informasi yang kontradiktif sering diberikan kepada kami.

Mari kita lihat beberapa contoh menggunakan database Stack Overflow 2010 pada SQL Server 2019 CU 14, dengan kompatibilitas database diatur ke 150.

Untuk memulai, kita memerlukan indeks nonclustered baru:

CREATE INDEX [IX dbo.Users Reputation (DisplayName)] 
ON dbo.Users (Reputation) 
INCLUDE (DisplayName);

1. Parameterisasi Sederhana Diterapkan

Contoh kueri pertama ini menggunakan parameterisasi sederhana :

SELECT U.DisplayName 
FROM dbo.Users AS U 
WHERE U.Reputation = 999;

Perkiraan (pra-eksekusi) rencana memiliki elemen terkait parameterisasi berikut:

Estimasi properti parameterisasi paket

Perhatikan @1 parameter diperkenalkan di mana-mana kecuali teks kueri yang ditampilkan di bagian atas.

sebenarnya (pasca-eksekusi) rencana memiliki:

Properti parameterisasi paket aktual

Perhatikan jendela properti sekarang telah kehilangan ParameterizedText elemen, sambil mendapatkan informasi tentang nilai runtime parameter. Teks kueri berparameter sekarang ditampilkan di bagian atas jendela dengan ‘@1 ' bukannya '999'.

2. Parameterisasi Sederhana Tidak Diterapkan

Contoh kedua ini tidak gunakan parameterisasi sederhana:

-- Projecting an extra column
SELECT 
    U.DisplayName, 
    U.CreationDate -- NEW
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Perkiraan rencana menunjukkan:

Perkiraan paket tanpa parameter

Kali ini, parameter @1 hilang dari Pencarian Indeks tooltip, tetapi teks berparameter dan elemen daftar parameter lainnya sama seperti sebelumnya.

Mari kita lihat yang sebenarnya rencana eksekusi:

Paket non-parameter sebenarnya

Hasilnya sama dengan aktual berparameter sebelumnya rencana, kecuali sekarang Index Seek tooltip menampilkan nilai non-parameter '999'. Teks kueri yang ditampilkan di bagian atas menggunakan @1 penanda parameter. Jendela properti juga menggunakan @1 dan menampilkan nilai runtime parameter.

Kueri bukan pernyataan berparameter meskipun semua bukti sebaliknya.

3. Parameterisasi Gagal

Contoh ketiga saya juga tidak diparameterisasi oleh server:

-- LOWER function used
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Perkiraan rencananya adalah:

Perkiraan parameter rencana gagal

Tidak disebutkan tentang @1 parameter di mana saja sekarang, dan Daftar Parameter bagian dari jendela properti tidak ada.

sebenarnya rencana eksekusinya sama, jadi saya tidak akan repot-repot menunjukkannya.

4. Paket Parameter Paralel

Saya ingin menunjukkan satu contoh lagi menggunakan paralelisme dalam rencana eksekusi. Perkiraan biaya kueri pengujian saya yang rendah berarti kami perlu menurunkan ambang biaya untuk paralelisme menjadi 1:

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 1;
RECONFIGURE;

Contoh kali ini sedikit lebih rumit:

SELECT 
    U.DisplayName 
FROM dbo.Users AS U 
WHERE 
    U.Reputation >= 5 
    AND U.DisplayName > N'ZZZ' 
ORDER BY 
    U.Reputation DESC;

Perkiraan rencana eksekusi adalah:

Perkiraan rencana parameter paralel

Teks kueri di bagian atas tetap tidak berparameter sementara yang lainnya. Ada dua penanda parameter sekarang, @1 dan @2 , karena parameterisasi sederhana menemukan dua nilai literal yang sesuai.

sebenarnya rencana eksekusi mengikuti pola contoh 1:

Paket parameter paralel aktual

Teks kueri di bagian atas sekarang diparameterisasi dan jendela properti berisi nilai parameter waktu proses. Paket paralel ini (dengan a Urut operator) pasti diparameterisasi oleh server menggunakan parameterisasi sederhana .

Metode yang Dapat Diandalkan

Ada alasan untuk semua perilaku yang ditunjukkan sejauh ini, dan beberapa lagi selain itu. Saya akan mencoba menjelaskan banyak dari ini di bagian selanjutnya dari seri ini ketika saya membahas kompilasi rencana.

Sementara itu, situasi showplan pada umumnya, dan SSMS pada khususnya, kurang ideal. Ini membingungkan bagi orang-orang yang telah bekerja dengan SQL Server sepanjang karir mereka. Penanda parameter mana yang Anda percayai, dan mana yang Anda abaikan?

Ada beberapa metode yang dapat diandalkan untuk menentukan apakah pernyataan tertentu memiliki parameterisasi sederhana yang berhasil diterapkan atau tidak.

Toko Kueri

Saya akan mulai dengan salah satu yang paling nyaman, toko kueri. Sayangnya, tidak selalu semudah yang Anda bayangkan.

Anda harus mengaktifkan fitur penyimpanan kueri untuk konteks basis data di mana pernyataan dieksekusi dan OPERATION_MODE harus disetel ke READ_WRITE , memungkinkan penyimpanan kueri untuk mengumpulkan data secara aktif.

Setelah memenuhi ketentuan ini, output showplan pasca-eksekusi berisi atribut tambahan, termasuk StatementParameterizationType . Seperti namanya, ini berisi kode yang menjelaskan jenis parameterisasi yang digunakan untuk pernyataan.

Itu terlihat di jendela properti SSMS ketika simpul akar rencana dipilih:

StatementParameterizationType

Nilai didokumentasikan dalam sys.query_store_query :

  • 0 – Tidak ada
  • 1 – Pengguna (parameterisasi eksplisit)
  • 2 – Parameterisasi sederhana
  • 3 – Parameterisasi paksa

Atribut bermanfaat ini hanya muncul di SSMS saat sebenarnya paket diminta dan hilang saat perkiraan rencana dipilih. Penting untuk diingat bahwa rencana harus di-cache . Meminta perkiraan paket dari SSMS tidak men-cache paket yang dihasilkan (sejak SQL Server 2012).

Setelah paket di-cache, StatementParameterizationType muncul di tempat biasa, termasuk melalui sys.dm_exec_query_plan .

Anda juga dapat mempercayai jenis parameterisasi tempat lain yang dicatat di penyimpanan kueri, seperti query_parameterization_type_desc kolom di sys.query_store_query .

Satu peringatan penting. Saat kueri menyimpan OPERATION_MODE diatur ke READ_ONLY , StatementParameterizationType atribut masih diisi di SSMS sebenarnya rencana—tetapi selalu nol —memberikan kesan yang salah bahwa pernyataan itu tidak diparameterisasi padahal seharusnya demikian.

Jika Anda senang mengaktifkan penyimpanan kueri, yakin itu membaca-tulis, dan hanya melihat rencana pasca-eksekusi di SSMS, ini akan berhasil untuk Anda.

Predikat Paket Standar

Teks kueri yang ditampilkan di bagian atas jendela showplan grafis di SSMS tidak dapat diandalkan, seperti yang ditunjukkan oleh contoh. Anda juga tidak dapat mengandalkan ParameterList ditampilkan di Properti jendela ketika simpul akar rencana dipilih. Teks Berparameter atribut ditampilkan untuk perkiraan rencana saja juga tidak konklusif.

Namun, Anda dapat mengandalkan properti yang terkait dengan masing-masing operator paket. Contoh yang diberikan menunjukkan bahwa ini ada di tooltips saat mengarahkan kursor ke operator.

Predikat yang berisi penanda parameter seperti @1 atau @2 menunjukkan rencana parameter. Operator yang paling mungkin berisi parameter adalah Pemindaian Indeks , Pencarian Indeks , dan Filter .

Predikat dengan penanda parameter

Jika penomoran dimulai dengan @1 , ia menggunakan parameterisasi sederhana . Parameterisasi paksa dimulai dengan @0 . Saya harus menyebutkan skema penomoran yang didokumentasikan di sini dapat berubah sewaktu-waktu:

Ubah peringatan

Namun demikian, ini adalah metode yang saya gunakan paling sering untuk menentukan apakah suatu rencana tunduk pada parameterisasi sisi server. Biasanya cepat dan mudah untuk memeriksa rencana secara visual untuk predikat yang berisi penanda parameter. Metode ini juga berfungsi untuk kedua jenis paket, perkiraan dan sebenarnya .

Objek Manajemen Dinamis

Ada beberapa cara untuk menanyakan cache rencana dan DMO terkait untuk menentukan apakah pernyataan telah diparameterisasi. Secara alami, kueri ini hanya berfungsi pada paket dalam cache, sehingga pernyataan harus telah dieksekusi hingga selesai, di-cache, dan kemudian tidak dihapus karena alasan apa pun.

Pendekatan yang paling langsung adalah mencari Adhoc rencanakan menggunakan kecocokan tekstual SQL yang tepat dengan pernyataan minat. Adhoc rencananya akan menjadi kulit berisi ParameterizedPlanHandle jika pernyataan tersebut diparameterisasi oleh server. Pegangan rencana kemudian digunakan untuk menemukan Disiapkan rencana. Sebuah Adhoc rencana tidak akan ada jika pengoptimalan untuk beban kerja ad hoc diaktifkan, dan pernyataan yang dimaksud hanya dijalankan sekali.

Jenis pertanyaan ini sering berakhir dengan merobek-robek sejumlah besar XML dan memindai seluruh cache paket setidaknya sekali. Juga mudah untuk mendapatkan kode yang salah, paling tidak karena paket dalam cache mencakup seluruh batch. Sebuah batch mungkin berisi beberapa pernyataan, yang masing-masing mungkin atau mungkin tidak diparameterisasi. Tidak semua DMO bekerja pada perincian yang sama (batch atau pernyataan) sehingga cukup mudah untuk lepas.

Cara yang efisien untuk membuat daftar pernyataan minat, bersama dengan fragmen rencana hanya untuk pernyataan individu tersebut, ditunjukkan di bawah ini:

SELECT
    StatementText =
        SUBSTRING(T.[text], 
            1 + (QS.statement_start_offset / 2), 
            1 + ((QS.statement_end_offset - 
                QS.statement_start_offset) / 2)),
    IsParameterized = 
        IIF(T.[text] LIKE N'(%',
            'Yes',
            'No'),
    query_plan = 
        TRY_CONVERT(xml, P.query_plan)
FROM sys.dm_exec_query_stats AS QS
CROSS APPLY sys.dm_exec_sql_text (QS.[sql_handle]) AS T
CROSS APPLY sys.dm_exec_text_query_plan (
    QS.plan_handle, 
    QS.statement_start_offset, 
    QS.statement_end_offset) AS P
WHERE 
    -- Statements of interest
    T.[text] LIKE N'%DisplayName%Users%'
    -- Exclude queries like this one
    AND T.[text] NOT LIKE N'%sys.dm%'
ORDER BY
    QS.last_execution_time ASC,
    QS.statement_start_offset ASC;

Sebagai ilustrasi, mari kita jalankan satu kumpulan yang berisi empat contoh dari sebelumnya:

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Example 1
SELECT U.DisplayName 
FROM dbo.Users AS U 
WHERE U.Reputation = 999;
 
-- Example 2
SELECT 
    U.DisplayName, 
    U.CreationDate 
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
 
-- Example 3
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
 
-- Example 4
SELECT 
    U.DisplayName 
FROM dbo.Users AS U 
WHERE 
    U.Reputation >= 5 
    AND U.DisplayName > N'ZZZ' 
ORDER BY 
    U.Reputation DESC;
GO

Keluaran dari kueri DMO adalah:

keluaran kueri DMO

Ini mengonfirmasi bahwa hanya contoh 1 dan 4 yang berhasil diparameterisasi.

Penghitung Kinerja

Penghitung kinerja Statistik SQL dapat digunakan untuk mendapatkan wawasan mendetail tentang aktivitas parameterisasi untuk keduanya perkiraan dan sebenarnya rencana. Penghitung yang digunakan tidak memiliki cakupan per sesi, jadi Anda harus menggunakan instance pengujian tanpa aktivitas bersamaan lainnya untuk mendapatkan hasil yang akurat.

Saya akan melengkapi informasi penghitung parameterisasi dengan data dari sys.dm_exec_query_optimizer_info DMO untuk menyediakan statistik pada rencana sepele juga.

Diperlukan kehati-hatian untuk mencegah pernyataan yang membaca informasi penghitung agar tidak memodifikasi penghitung itu sendiri. Saya akan mengatasinya dengan membuat beberapa prosedur tersimpan sementara:

CREATE PROCEDURE #TrivialPlans
AS
SET NOCOUNT ON;
 
SELECT
    OI.[counter],
    OI.occurrence
FROM sys.dm_exec_query_optimizer_info AS OI
WHERE
    OI.[counter] = N'trivial plan';
GO
CREATE PROCEDURE #PerfCounters
AS
SET NOCOUNT ON;
 
SELECT
    PC.[object_name],
    PC.counter_name,
    PC.cntr_value
FROM 
    sys.dm_os_performance_counters AS PC
WHERE 
    PC.counter_name LIKE N'%Param%';

Script untuk menguji pernyataan tertentu akan terlihat seperti ini:

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
EXECUTE #PerfCounters;
EXECUTE #TrivialPlans;
GO
SET SHOWPLAN_XML ON;
GO
-- The statement(s) under test:
-- Example 3
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
GO
SET SHOWPLAN_XML OFF;
GO
EXECUTE #TrivialPlans;
EXECUTE #PerfCounters;

Komentari SHOWPLAN_XML batch keluar untuk menjalankan pernyataan target dan dapatkan aktual rencana. Biarkan di tempat untuk perkiraan rencana eksekusi.

Menjalankan semuanya seperti yang tertulis memberikan hasil berikut:

Hasil tes penghitung kinerja

Saya telah menyoroti di atas di mana nilai berubah saat menguji contoh 3.

Peningkatan penghitung "rencana sepele" dari 1050 menjadi 1051 menunjukkan rencana sepele ditemukan untuk pernyataan pengujian.

Penghitung parameterisasi sederhana meningkat 1 untuk upaya dan kegagalan, menunjukkan SQL Server mencoba membuat parameter pernyataan, tetapi gagal.

Akhir Bagian 3

Di bagian selanjutnya dari seri ini, saya akan menjelaskan hal-hal aneh yang telah kita lihat dengan menjelaskan bagaimana parameterisasi sederhana dan rencana sepele berinteraksi dengan proses kompilasi.

Jika Anda mengubah ambang biaya untuk paralelisme untuk menjalankan contoh, ingatlah untuk mengatur ulang (milik saya disetel ke 50):

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Solusi pembaca untuk tantangan Kepulauan Khusus

  2. Cara Menemukan dan Menutupi PII di Elasticsearch

  3. Meminimalkan dampak pelebaran kolom IDENTITAS – bagian 2

  4. Cara Membatasi Hasil di T-SQL

  5. Cara Menginstal Cassandra v3 di CentOS 6