Kami sedang mengajar IEPTO2 di Dublin minggu ini (dan jika Irlandia tidak ada dalam daftar tempat yang harus Anda kunjungi seumur hidup ini, Anda harus menambahkannya… luar biasa di sini) dan hari ini saya menyelesaikan modul Analisis Rencana Kueri. Satu hal yang saya bahas adalah hal-hal menarik yang dapat Anda temukan dalam rencana kueri, misalnya:
- NoJoinPredicate (2005 dan lebih tinggi)
- ColumnsWithNoStatistics (2005 dan lebih tinggi)
- UnmatchedIndexes (2008 dan lebih tinggi)
- PlanAffectingConvert (2012 dan lebih tinggi)
Atribut ini bagus untuk dicari saat Anda melihat satu paket, atau sekumpulan paket, saat Anda menyetelnya. Tetapi jika Anda ingin sedikit lebih proaktif, Anda dapat mulai menambang cache paket dan mencarinya di sana. Tentu saja, melakukannya memerlukan penulisan beberapa XQuery, karena rencananya adalah XML (untuk detail tentang skema showplan, lihat:http://schemas.microsoft.com/sqlserver/2004/07/showplan/). Saya tidak menyukai XML, meskipun bukan karena kurang mencoba, dan ketika salah satu peserta bertanya apakah Anda dapat menangkap kueri yang memiliki atribut NoJoinPredicate melalui Acara yang Diperpanjang, saya berpikir, “Ide yang bagus, saya harus memeriksa .”
Benar saja, ada Acara untuk itu. Ada Acara untuk keempat acara yang saya sebutkan di atas:
- predikat_join_hilang
- statistik_kolom_hilang
- unmatched_filtered_indexes
- rencana_mempengaruhi_konversi
Bagus. Menyiapkan ini dalam sesi Acara yang Diperpanjang cukup mudah. Dalam hal ini, saya akan merekomendasikan menggunakan target event_file, karena Anda mungkin akan memulai sesi acara dan membiarkannya berjalan sebentar sebelum Anda kembali dan meninjau hasilnya. Saya telah menyertakan beberapa tindakan, dengan harapan peristiwa ini tidak memicu itu sering, jadi kami tidak menambahkan terlalu banyak overhead di sini. Saya menyertakan sql_text meskipun itu bukan tindakan yang benar-benar harus Anda andalkan. Jonathan telah membahas ini sebelumnya, tetapi sql_text hanya memberi Anda inputbuffer, jadi Anda mungkin tidak mendapatkan cerita lengkap untuk kueri tersebut. Untuk alasan itu, saya juga menyertakan plan_handle. Peringatannya adalah, tergantung pada saat Anda mencari paket tersebut, paket tersebut mungkin tidak lagi ada di cache paket.
-- Remove event session if it exists IF EXISTS (SELECT 1 FROM [sys].[server_event_sessions] WHERE [name] = 'InterestingPlanEvents') BEGIN DROP EVENT SESSION [InterestingPlanEvents] ON SERVER END GO -- Define event session CREATE EVENT SESSION [InterestingPlanEvents] ON SERVER ADD EVENT sqlserver.missing_column_statistics ( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)) AND [sqlserver].[database_id]>(4)) ), ADD EVENT sqlserver.missing_join_predicate ( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([sqlserver].[is_system]=(0) AND [sqlserver].[database_id]>(4)) ), ADD EVENT sqlserver.plan_affecting_convert ( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)) AND [sqlserver].[database_id]>(4)) ), ADD EVENT sqlserver.unmatched_filtered_indexes ( ACTION(sqlserver.plan_handle,sqlserver.sql_text) WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)) AND [sqlserver].[database_id]>(4)) ) ADD TARGET package0.event_file ( SET filename=N'C:\temp\InterestingPlanEvents' /* change location if appropriate */ ) WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=ON,STARTUP_STATE=OFF) GO -- Start the event session ALTER EVENT SESSION [InterestingPlanEvents] ON SERVER STATE=START; GO
Setelah sesi acara aktif dan berjalan, kami dapat membuat acara ini dengan kode contoh di bawah ini. Perhatikan bahwa kode ini mengasumsikan instalasi baru AdventureWorks2014. Jika tidak memilikinya, Anda mungkin tidak melihat peristiwa missing_column_statistics diaktifkan jika Anda menanyakan kolom [HireDate] di [HumanResources].[Employee].
-- These queries assume a FRESH restore of AdventureWorks2014 ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS OFF; GO USE [AdventureWorks2014]; GO CREATE INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader] ( [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate] ) WHERE [SubTotal] > 10000.00; GO /* No join predicate NOTE: We clear procedure here because the event ONLY fires for the *initial* compilation */ DBCC FREEPROCCACHE; /* Not for production use */ SELECT [h].[SalesOrderID], [d].[SalesOrderDetailID], [h].[CustomerID] FROM [Sales].[SalesOrderDetail] [d], [Sales].[SalesOrderHeader] [h] WHERE [d].[ProductID] = 897; GO -- Columns with no statistics SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate] FROM [HumanResources].[Employee] WHERE [HireDate] >= '2013-01-01'; GO -- Unmatched Index DECLARE @Total MONEY = 10000.00; SELECT [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate] FROM [Sales].[SalesOrderHeader] WHERE [SubTotal] > @Total; GO -- Plan Affecting Convert SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate] FROM [HumanResources].[Employee] WHERE [NationalIDNumber] = 345106466; GO ALTER EVENT SESSION [InterestingPlanEvents] ON SERVER STATE=STOP; GO DROP EVENT SESSION [InterestingPlanEvents] ON SERVER; GO
CATATAN:SETELAH Anda selesai menarik rencana dari cache, Anda dapat menjalankan pernyataan ALTER untuk mengaktifkan opsi buat statistik otomatis. Melakukannya pada titik ini akan menghapus cache paket dan Anda harus memulai dari awal dengan pengujian Anda. (Dan juga tunggu sampai Anda selesai menghapus indeks.)
ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS ON; GO DROP INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader]; GO
Karena saya telah menghentikan sesi acara, saya akan membuka file output di SSMS untuk melihat apa yang kami tangkap:
Keluaran dari Acara yang Diperpanjang
Untuk kueri pertama kami dengan predikat bergabung yang hilang, kami memiliki satu peristiwa yang muncul, dan saya dapat melihat teks untuk kueri di bidang sql_text. Namun, yang sebenarnya saya inginkan adalah melihat rencananya juga, jadi saya dapat mengambil plan_handle dan memeriksa sys.dm_exec_query_plan:
SELECT query_plan FROM sys.dm_exec_query_plan (0x06000700E2200333405DD12C0000000001000000000000000000000000000000000000000000000000000000);
Dan membukanya di SQL Sentry Plan Explorer:
Predikat Gabung Tidak Ada
Paket tersebut memiliki indikator visual dari predikat gabungan yang hilang di loop bersarang (X merah), dan jika saya mengarahkan kursor ke atasnya, saya melihat peringatan (dan ada dalam XML untuk paket tersebut). Luar biasa…Saya sekarang dapat berbicara dengan pengembang saya tentang menulis ulang kueri ini.
Acara berikutnya adalah untuk statistik kolom yang hilang. Saya sepenuhnya memaksakan situasi ini dengan mematikan AUTO_CREATE_STATISTICS untuk database AdventureWorks2014. Saya tidak merekomendasikan ini dengan cara, bentuk, atau bentuk apa pun. Opsi ini diaktifkan secara default dan saya sarankan untuk selalu membiarkannya diaktifkan. Namun, mematikannya adalah cara termudah untuk menghasilkan acara ini. Saya kembali memiliki kueri di bidang sql_text, tetapi saya akan menggunakan plan_handle lagi untuk menarik paket:
SELECT query_plan FROM sys.dm_exec_query_plan (0x060007004448323810921C360000000001000000000000000000000000000000000000000000000000000000);
Statistik Tidak Ada
Dan kami kembali memiliki isyarat visual (segitiga kuning dengan tanda seru) untuk menunjukkan bahwa ada masalah dengan rencana, dan sekali lagi itu ada di XML. Dari sini, pertama-tama saya akan memeriksa untuk melihat apakah AUTO_CREATE_STATISTICS dinonaktifkan, dan jika tidak, saya akan mulai menjalankan kueri di Management Studio untuk melihat apakah saya dapat membuat ulang peringatan (dan memaksa statistik untuk dibuat).
Sekarang, acara yang tersisa sedikit lebih menarik.
Anda akan melihat bahwa kami memiliki tiga peristiwa unmatched_filtered_indexes. Saya belum menentukan alasannya, tetapi saya sedang mengerjakannya dan akan memposting di komentar jika/ketika saya menyelesaikannya. Untuk saat ini cukuplah saya memiliki event tersebut, dan di dalam event tersebut kita juga dapat melihat informasi objek sehingga saya mengetahui indeks yang dimaksud:
Indeks NCI_SalesOrderHeader dirujuk oleh peristiwa indeks yang hilang
Dan saya dapat kembali menggunakan plan_handle untuk menemukan paket kueri:
Indeks yang tidak cocok
Kali ini saya melihat peringatan di operator SELECT, jadi saya tahu ada sesuatu yang perlu saya selidiki lebih lanjut. Dalam hal ini, Anda memiliki opsi agar pengoptimal menggunakan indeks yang difilter saat Anda menggunakan parameter, dan saya sarankan membaca posting Aaron untuk informasi lebih lanjut tentang menggunakan indeks yang difilter.
Terakhir, kami memiliki sembilan acara untuk plan_affecting_convert. Apa apaan? Saya masih mencari tahu yang ini, tetapi saya menggunakan opsi Lacak Kausalitas untuk sesi acara saya (saat menguji) untuk mengonfirmasi bahwa semua acara adalah bagian dari tugas yang sama (mereka). Jika Anda melihat elemen ekspresi dalam output, Anda melihat bahwa itu sedikit berubah (seperti halnya compile_time), dan ini muncul saat Anda melihat detail peringatan di Plan Explorer SQL Sentry (lihat tangkapan layar kedua di bawah). Dalam output acara, elemen ekspresi tidak beri tahu kami kolom apa yang terlibat, yang merupakan informasi awal tetapi tidak cukup, jadi sekali lagi kami harus mendapatkan rencananya:
SELECT query_plan FROM sys.dm_exec_query_plan (0x0600070023747010E09E1C360000000001000000000000000000000000000000000000000000000000000000);
Rencana yang Mempengaruhi Konversi
Detail konversi dari paket
Kita kembali melihat teman kita, segitiga kuning, di operator SELECT, dan di dalam XML kita dapat menemukan atribut PlanAffectingConvert. Atribut ini ditambahkan dalam skema showplan SQL Server 2012, jadi jika Anda menjalankan versi yang lebih lama, Anda tidak akan melihat ini dalam paket. Menyelesaikan peringatan ini mungkin memerlukan sedikit lebih banyak pekerjaan – Anda perlu memahami di mana Anda mengalami ketidakcocokan tipe data dan mengapa, dan kemudian mulai memodifikasi kode atau skema… keduanya dapat menemui hambatan. Jonathan memiliki postingan yang membahas konversi implisit secara lebih rinci, yang merupakan tempat yang baik untuk memulai jika Anda belum pernah menangani masalah konversi sebelumnya.
Ringkasan
Pustaka Acara yang Diperpanjang dari acara terus berkembang, dan satu hal yang perlu dipertimbangkan saat memecahkan masalah di SQL Server adalah apakah Anda bisa mendapatkan informasi yang Anda cari dengan cara lain. Mungkin karena lebih mudah (saya yakin lebih suka XE daripada XML!), Atau karena lebih efisien, atau memberi Anda lebih banyak detail. Apakah Anda secara proaktif mencari masalah kueri di lingkungan Anda, atau bereaksi terhadap masalah yang telah dilaporkan seseorang tetapi Anda kesulitan menemukannya, acara yang diperluas adalah opsi yang layak untuk dipertimbangkan, terutama karena lebih banyak fitur baru ditambahkan ke SQL Server.