Panduan Rencana SQL Server dan Praktik Lain Bukan yang Terbaik

Biasanya, posting tentang optimisasi kueri memberi tahu Anda bagaimana melakukan hal yang benar untuk membantu optimizer kueri memilih rencana eksekusi terbaik: gunakan ekspresi SARGable di WHERE, ambil hanya kolom yang Anda butuhkan, gunakan indeks yang dibentuk dengan baik yang didefragmentasi dan dengan statistik yang diperbarui.



Hari ini saya ingin berbicara tentang sesuatu yang lain - tentang sesuatu yang sama sekali bukan milik praktik terbaik, dengan bantuan yang sangat mudah untuk menembak diri sendiri di kaki dan membuat permintaan yang sebelumnya dieksekusi lebih lambat, atau tidak lagi dijalankan sama sekali karena kesalahan ... Ini tentang petunjuk dan panduan rencana.



Petunjuk adalah petunjuk untuk pengoptimal kueri, daftar lengkap dapat ditemukan di MSDN . Beberapa di antaranya benar-benar petunjuk (misalnya, Anda dapat menentukan OPTION (MAXDOP 4)) sehingga kueri dapat dieksekusi dengan derajat paralelisme maks = 4, tetapi tidak ada jaminan bahwa SQL Server akan menghasilkan rencana paralel dengan petunjuk ini sama sekali.



Bagian lainnya adalah panduan langsung untuk bertindak. Misalnya, jika Anda menulis OPSI (BERGABUNG DENGAN HASH), maka SQL Server akan membangun sebuah rencana tanpa NESTED LOOPS dan MERGE JOINs. Dan tahukah Anda apa yang akan terjadi jika ternyata tidak mungkin membangun rencana dengan hanya bergabung dengan hash? Pengoptimal akan berkata begitu - Saya tidak dapat membuat rencana dan kueri tidak akan dieksekusi.



Masalahnya adalah tidak diketahui secara pasti (setidaknya bagi saya) petunjuk mana yang merupakan petunjuk bahwa pengoptimal dapat memalu; dan petunjuk mana yang merupakan petunjuk manual yang dapat menyebabkan permintaan macet jika terjadi kesalahan. Tentunya sudah ada beberapa koleksi siap pakai di mana ini dijelaskan, tetapi ini dalam hal apapun bukan informasi resmi dan dapat berubah setiap saat.



Panduan Rencana adalah hal semacam itu (yang saya tidak tahu bagaimana menerjemahkan dengan benar) yang memungkinkan Anda untuk mengikat serangkaian petunjuk spesifik untuk permintaan tertentu, teks yang Anda tahu. Ini mungkin relevan jika Anda tidak dapat secara langsung memengaruhi teks permintaan yang dihasilkan oleh ORM, misalnya.



Petunjuk dan panduan rencana sama sekali bukan praktik terbaik, melainkan praktik yang baik untuk menghilangkan petunjuk dan panduan ini, karena distribusi data dapat berubah, tipe data dapat berubah dan jutaan hal lainnya dapat terjadi, karena itu pertanyaan Anda dengan petunjuk akan bekerja lebih buruk daripada tanpa mereka, dan dalam beberapa kasus akan berhenti bekerja sama sekali. Anda harus seratus persen sadar akan apa yang Anda lakukan dan mengapa.



Sekarang penjelasan kecil tentang mengapa saya masuk ke ini.



Saya punya meja lebar dengan banyak bidang nvarchar dengan ukuran berbeda - mulai dari 10 hingga maks. Dan ada banyak pertanyaan untuk tabel ini, yang CHARINDEX mencari kemunculan substring dalam satu atau lebih kolom ini. Misalnya, ada permintaan yang terlihat seperti ini:



SELECT *
FROM table
WHERE CHARINDEX(N' ', column)>1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET x ROWS FETCH NEXT y ROWS ONLY


Tabel memiliki indeks berkerumun pada Id dan indeks non-berkerumun pada kolom yang tidak unik. Seperti yang Anda sendiri pahami, tidak ada artinya dari semua ini, karena di WHERE kami menggunakan CHARINDEX, yang jelas-jelas bukan SARGable. Untuk menghindari kemungkinan masalah dengan SB, saya akan mensimulasikan situasi ini pada database terbuka StackOverflow2013, yang dapat ditemukan di sini .



Pertimbangkan tabel dbo.Posts, yang hanya memiliki indeks yang dikelompokkan berdasarkan ID dan kueri seperti ini:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


Untuk mencocokkan database asli saya, saya membuat indeks pada kolom Judul:



CREATE INDEX ix_Title ON dbo.Posts (Title);


Akibatnya, tentu saja, kami mendapatkan rencana eksekusi yang benar-benar logis, yang terdiri dari pemindaian indeks berkerumun di arah yang berlawanan:











Dan, memang, dilakukan dengan cukup baik:

Tabel 'Posting'. Pindai hitungan 1, pembacaan logis 516, pembacaan fisik 0, pembacaan pembacaan awal 0, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan pembacaan lob 0 0.

Waktu Eksekusi Server SQL:

Waktu CPU = 16 ms


Tetapi apa yang terjadi jika, alih-alih kata umum 'Data', kami mencari sesuatu yang lebih jarang? Misalnya, N'Aptana '(tidak tahu apa itu). Rencananya, tentu saja, akan tetap sama, tetapi statistik eksekusi, ahem, akan agak berubah:

Tabel 'Posting'. Pindai hitungan 1, bacaan logis 253191, bacaan fisik 113, baca-depan baca 224602, lob logis berbunyi 0, lob fisik baca 0, lob baca-depan berbunyi 0.

Waktu Eksekusi Server SQL:

Waktu CPU = 2563 ms


Dan ini juga logis - kata ini jauh lebih jarang dan SQL Server harus memindai lebih banyak data untuk menemukan 25 baris dengannya. Tapi entah kenapa itu tidak keren, bukan?

Dan saya sedang membuat indeks non-cluster. Mungkin akan lebih baik jika SQL Server menggunakannya? Dia sendiri tidak akan menggunakannya, jadi saya menambahkan petunjuk:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Title)));


Dan, ada sesuatu yang benar-benar menyedihkan. Statistik eksekusi:

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Posts'. Scan count 5, logical reads 109312, physical reads 5, read-ahead reads 104946, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 35031 ms


dan rencananya:







Sekarang rencana pelaksanaannya paralel dan ada dua macam, keduanya dengan tumpahan di tempdb. Omong-omong, perhatikan jenis pertama, yang dilakukan setelah pemindaian indeks nonclustered, sebelum Pencarian Kunci - ini adalah optimasi SQL Server khusus yang mencoba untuk mengurangi jumlah Random I / O - pencarian kunci dilakukan dalam urutan menaik dari kunci indeks berkerumun. Anda dapat membaca lebih lanjut tentang ini di sini .



Jenis kedua diperlukan untuk memilih 25 baris dalam urutan menurun. By the way, SQL Server bisa menebak bahwa itu harus mengurutkan berdasarkan Id lagi, hanya dalam urutan menurun dan melakukan pencarian kunci di arah "berlawanan", mengurutkan dalam urutan menurun, bukan naik, dari kunci indeks berkerumun di awal.



Saya tidak memberikan statistik tentang pelaksanaan kueri dengan petunjuk pada indeks yang tidak tercakup dengan pencarian dengan entri 'Data'. Pada hard drive setengah mati saya di laptop, butuh lebih dari 16 menit dan saya tidak berpikir untuk mengambil screenshot. Maaf, saya tidak ingin menunggu selama itu lagi.

Tapi bagaimana dengan permintaan itu? Apakah indeks berkerumun memindai mimpi terakhir, dan Anda tidak dapat melakukan sesuatu lebih cepat?



Bagaimana jika saya mencoba menghindari segala macam, saya berpikir dan membuat indeks yang tidak tercakup, yang, secara umum, bertentangan dengan apa yang biasanya dianggap sebagai praktik terbaik untuk indeks yang tidak tercakup:



CREATE INDEX ix_Id_Title ON dbo.Posts (Id DESC, Title);


Sekarang kita menggunakan petunjuk untuk memberitahu SQL Server untuk menggunakannya:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Id_Title)));


Oh, itu bekerja dengan baik:





Tabel 'Posting'. Pindai hitungan 1, bacaan logis 6259, bacaan fisik 0, bacalah bacaan 7816, bacaan logis lob 0, bacaan fisik lob 0, bacaan bacaan lob depan 0.

Waktu Eksekusi Server SQL:

Waktu CPU = 1734 ms


Gain dalam waktu prosesor tidak besar, tetapi Anda harus membaca lebih sedikit - tidak buruk. Bagaimana dengan 'Data' yang sering?

Tabel 'Posting'. Pindai hitungan 1, pembacaan logis 208, pembacaan fisik 0, pembacaan pembacaan ke depan 0, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan pembacaan lob ke depan 0.

Waktu Eksekusi SQL Server:

Waktu CPU = 0 ms


Wow, itu bagus juga. Sekarang, karena permintaan berasal dari ORM dan kami tidak dapat mengubah teksnya, kami perlu mencari cara untuk "memakukan" indeks ini ke permintaan. Dan panduan rencana datang untuk menyelamatkan.



Prosedur tersimpan sp_create_plan_guide ( MSDN ) digunakan untuk membuat panduan paket .



Mari kita pertimbangkan secara rinci:



sp_create_plan_guide [ @name = ] N'plan_guide_name'  
    , [ @stmt = ] N'statement_text'  
    , [ @type = ] N'{ OBJECT | SQL | TEMPLATE }'  
    , [ @module_or_batch = ]  
      {   
        N'[ schema_name. ] object_name'  
        | N'batch_text'  
        | NULL  
      }  
    , [ @params = ] { N'@parameter_name data_type [ ,...n ]' | NULL }   
    , [ @hints = ] { 
                 N'OPTION ( query_hint [ ,...n ] )'   
                 | N'XML_showplan'  
                 | NULL 
      }  


nama - nama panduan rencana yang jelas dan unik

stmt- ini adalah permintaan yang harus Anda tambahkan petunjuknya. Penting untuk diketahui di sini bahwa permintaan ini harus ditulis PERSIS sama dengan permintaan yang berasal dari aplikasi. Ruang ganjil? Panduan Rencana tidak akan digunakan. Istirahat baris yang salah? Panduan Rencana tidak akan digunakan. Untuk membuat segalanya lebih mudah bagi Anda sendiri, ada "hack kehidupan" di mana saya akan kembali sedikit kemudian (dan yang saya temukan di sini ).



Tipe - menunjukkan di mana permintaan ditentukan stmt... Jika ini merupakan bagian dari prosedur tersimpan, itu harus OBJECT; jika ini merupakan bagian dari batch dari beberapa permintaan, atau itu adalah permintaan ad-hoc, atau batch dari satu permintaan, harus ada SQL. Jika TEMPLATE diindikasikan di sini, ini adalah cerita terpisah tentang parameterisasi kueri, yang dapat Anda baca di MSDN .



@module_or_batch tergantung padaTipe. Jika sebuahTipe= 'OBJECT', ini harus menjadi nama prosedur yang tersimpan. Jika sebuahTipe= 'BATCH' - harus ada teks dari seluruh kumpulan, kata demi kata yang ditentukan dengan apa yang berasal dari aplikasi. Ruang ganjil? Nah, Anda sudah tahu. Jika NULL, maka kami menganggap bahwa ini adalah kumpulan dari satu permintaan dan cocok dengan yang ditentukanstmt dengan semua batasan.



params- semua parameter yang diteruskan ke permintaan bersama dengan tipe data harus dicantumkan di sini.



@ Petunjuk akhirnya adalah bagian yang bagus, di sini Anda perlu menentukan petunjuk yang akan ditambahkan ke permintaan. Di sini Anda dapat secara eksplisit memasukkan rencana eksekusi yang diperlukan dalam format XML, jika ada. Parameter ini juga bisa NULL, yang akan mengarah pada fakta bahwa SQL Server tidak akan menggunakan petunjuk yang secara eksplisit ditentukan dalam kueri distmt...



Jadi, kami membuat Panduan Rencana untuk kueri:



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N''Data'', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY';

exec sp_create_plan_guide @name = N'PG_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = NULL
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


Dan kami mencoba menjalankan permintaan:

SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


Wow, itu berhasil:







Dalam properti pernyataan SELECT terakhir, kita melihat:







Hebat, rencana giude telah diterapkan. Bagaimana jika Anda mencari 'Aptana' sekarang? Dan semuanya akan menjadi buruk - kita akan kembali ke pemindaian indeks berkerumun dengan semua konsekuensinya. Mengapa? Dan karena, panduan paket diterapkan ke kueri SPESIFIK, teks yang bertepatan satu dengan satu dengan yang mengeksekusi.



Untungnya bagi saya, sebagian besar permintaan pada sistem saya masuk dalam parameter. Saya tidak bekerja dengan kueri yang tidak diparameterisasi dan saya harap saya tidak perlu melakukannya. Bagi mereka, Anda dapat menggunakan templat (lihat sedikit lebih tinggi tentang TEMPLATE), Anda dapat mengaktifkan PARAMETERISASI PAKSA dalam database ( jangan lakukan ini tanpa memahami apa yang Anda lakukan !!! ) dan, mungkin, setelah itu, Anda akan dapat menautkan Panduan Rencana. Tetapi saya benar-benar belum mencobanya.



Dalam kasus saya, permintaan dijalankan dalam sesuatu seperti ini:



exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Aptana', @p1 = 0, @p2 = 25;


Oleh karena itu, saya membuat panduan paket yang sesuai:



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;';

exec sp_create_plan_guide @name = N'PG_paramters_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = N'@p0 nvarchar(250), @p1 int, @p2 int'
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


Dan, hore, semuanya berfungsi seperti yang disyaratkan:











Berada di luar kondisi rumah kaca, tidak selalu mungkin untuk menentukan parameter dengan benarstmtuntuk melampirkan panduan rencana ke permintaan, dan untuk ini ada "hack kehidupan" yang saya sebutkan di atas. Kami menghapus cache rencana, menghapus panduan, menjalankan kueri parameterisasi lagi dan mendapatkan rencana eksekusi serta plan_handle-nya dari cache.



Permintaan untuk ini dapat digunakan, misalnya, seperti ini:



SELECT qs.plan_handle, st.text, qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text (qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan (qs.plan_handle) qp






Kita sekarang dapat menggunakan prosedur tersimpan sp_create_plan_guide_from_handle untuk membuat panduan rencana dari rencana yang ada.



Dibutuhkan sebagai parameternama- nama panduan yang dibuat, @plan_handle - pegangan dari rencana eksekusi yang ada dan @statement_start_offset - yang mendefinisikan awal pernyataan dalam kumpulan yang menjadi pedoman pembuatan panduan tersebut.



Mencoba:



exec sp_create_plan_guide_from_handle N'PG_dboPosts_from_handle'  
    , 0x0600050018263314F048E3652102000001000000000000000000000000000000000000000000000000000000
    , NULL;


Dan sekarang di SSMS kita melihat apa yang kita miliki di Programmability -> Panduan Plan:







Sekarang rencana eksekusi saat ini telah "dipaku" untuk permintaan kami menggunakan Panduan Rencana 'PG_dboPosts_from_handle', tapi, yang paling penting, sekarang, seperti hampir semua objek dalam SSMS, kita dapat membuat skrip dan membuat ulang cara kita membutuhkannya.



RMB, Script -> Drop AND Create dan kami mendapatkan skrip siap pakai di mana kami perlu mengganti nilai parameter @hints dengan yang kami butuhkan, sehingga hasilnya kami dapatkan:



USE [StackOverflow2013]
GO

/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_control_plan_guide @operation = N'DROP', @name = N'[PG_dboPosts_from_handle]'
GO
/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_create_plan_guide @name = N'[PG_dboPosts_from_handle]', @stmt = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY', @type = N'SQL', @module_or_batch = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;', 
@params = N'@p0 nvarchar(250), @p1 int, @p2 int', 
@hints = N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'
GO


Kami menjalankan dan mengeksekusi kembali permintaan tersebut. Hore, semuanya berfungsi:







Jika Anda mengganti nilai parameter, semuanya bekerja dengan cara yang sama.



Harap dicatat bahwa hanya satu panduan yang dapat sesuai dengan satu pernyataan. Jika Anda mencoba menambahkan panduan lain ke pernyataan yang sama, Anda akan menerima pesan kesalahan.

Msg 10502, Level 16, Negara 1, Baris 1

Tidak dapat membuat panduan rencana 'PG_dboPosts_from_handle2' karena pernyataan yang ditentukan olehstmtdan @module_or_batch, atau dengan @plan_handle dan @statement_start_offset, cocok dengan panduan rencana yang ada 'PG_dboPosts_from_handle' dalam database. Jatuhkan panduan paket yang ada sebelum membuat panduan paket baru.


Hal terakhir yang ingin saya sebutkan adalah prosedur tersimpan sp_control_plan_guide .



Dengan bantuannya, Anda dapat menghapus, menonaktifkan, dan mengaktifkan Panduan Panduan - keduanya satu per satu, dengan nama, dan semua panduan (saya tidak yakin - semuanya sama sekali. Atau segala sesuatu dalam konteks database di mana prosedur dijalankan) - nilai digunakan untuk ini Parameter @operation - DROP ALL, DISABLE ALL, ENABLE ALL. Contoh penggunaan HP untuk paket spesifik diberikan di atas - Panduan Paket khusus dengan nama yang ditentukan dihapus.



Apakah mungkin dilakukan tanpa petunjuk dan panduan rencana?



Secara umum, jika menurut Anda pengoptimal kueri itu bodoh dan melakukan beberapa jenis permainan, dan Anda tahu cara terbaik, dengan probabilitas 99% Anda melakukan beberapa jenis permainan (seperti dalam kasus saya). Namun, dalam kasus ketika Anda tidak memiliki kemampuan untuk secara langsung mempengaruhi teks permintaan, panduan rencana yang memungkinkan Anda untuk menambahkan petunjuk pada permintaan bisa menjadi penyelamat. Misalkan kita memiliki kemampuan untuk menulis ulang teks permintaan sesuai kebutuhan - dapatkah ini mengubah sesuatu? Tentu! Bahkan tanpa menggunakan "eksotis" dalam bentuk pencarian teks lengkap, yang, pada kenyataannya, harus digunakan di sini. Misalnya, kueri tersebut memiliki statistik paket dan eksekusi yang sepenuhnya normal (untuk kueri):



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2
    WHERE CHARINDEX (N'Aptana', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




Tabel 'Posting'. Pindai hitungan 1, pembacaan logis 6250, pembacaan fisik 0, pembacaan pembacaan awal 0, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan pembacaan lob 0 0.

Waktu Eksekusi Server SQL:

Waktu CPU = 1500 ms


SQL Server pertama kali menemukan 25 pengidentifikasi yang diperlukan oleh indeks "bengkok" ix_Id_Title, dan hanya kemudian melakukan pencarian dalam indeks berkerumun menggunakan pengidentifikasi yang dipilih - bahkan lebih baik daripada dengan panduan ini! Tetapi apa yang terjadi jika kita menjalankan kueri pada 'Data' dan menampilkan 25 baris, mulai dari baris ke 20.000:



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2 
    WHERE CHARINDEX (N'Data', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 20000 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




Tabel 'Posting'. Pindai hitungan 1, pembacaan logis 5914, pembacaan fisik 0, pembacaan pembacaan awal 0, pembacaan logis lob 11, pembacaan fisik lob 0, pembacaan pembacaan lob 0 0.

Waktu Eksekusi Server SQL:

Waktu CPU = 1453 ms


exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Data', @p1 = 20000, @p2 = 25;




Table 'Posts'. Scan count 1, logical reads 87174, physical reads 0, read-ahead reads 0, lob logical reads 11, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 1437 ms


Ya, waktu prosesor adalah sama, karena dihabiskan untuk charindex, tetapi permintaan dengan panduan melakukan urutan lebih banyak dibaca, dan ini bisa menjadi masalah.



Biarkan saya meringkas hasil akhir. Petunjuk dan panduan dapat banyak membantu Anda di sini dan sekarang, tetapi mereka dapat dengan mudah membuat segalanya menjadi lebih buruk. Jika Anda secara eksplisit menentukan petunjuk dengan indeks dalam teks permintaan, dan kemudian menghapus indeks, kueri tidak bisa dieksekusi. Pada SQL Server 2017 saya, kueri dengan panduan ini, setelah menghapus indeks, dieksekusi dengan baik - panduan ini diabaikan, tetapi saya tidak dapat memastikan bahwa itu akan selalu seperti ini dan di semua versi SQL Server.



Tidak ada banyak informasi tentang panduan rencana dalam bahasa Rusia, jadi saya memutuskan untuk menulisnya sendiri. Anda bisa baca di sinitentang keterbatasan dalam menggunakan panduan rencana, khususnya tentang fakta bahwa kadang-kadang indikasi indeks secara eksplisit dengan petunjuk menggunakan PG dapat mengarah pada fakta bahwa permintaan akan turun. Saya berharap Anda tidak pernah menggunakannya, dan jika Anda harus - yah, semoga sukses - Anda tahu ke mana ini bisa mengarah.



All Articles