有時候,我們需要查看存儲過程的執行計划,那么我們有什么方式獲取存儲過程的歷史執行計划或當前的執行計划呢? 下面總結一下獲取存儲過程的執行計划的方法。
1:我們可以通過下面腳本查看存儲過程的執行計划,但是有時候,你會發現這種方式並不總是能夠獲取到存儲過程的執行計划。
SELECT
d.object_id ,
DB_NAME(d.database_id) DBName ,
OBJECT_NAME(object_id, database_id) 'SPName' ,
d.cached_time ,
d.last_execution_time ,
d.total_elapsed_time/1000000 AS total_elapsed_time,
d.total_elapsed_time / d.execution_count/1000000
AS [avg_elapsed_time] ,
d.last_elapsed_time/1000000 AS last_elapsed_time,
d.execution_count ,
d.total_physical_reads ,
d.last_physical_reads ,
d.total_logical_writes ,
d.last_logical_reads ,
et.text SQLText ,
eqp.query_plan executionplan
FROM sys.dm_exec_procedure_stats AS d
CROSS APPLY sys.dm_exec_sql_text(d.sql_handle) et
CROSS APPLY sys.dm_exec_query_plan(d.plan_handle) eqp
WHERE OBJECT_NAME(object_id, database_id) = 'xxxx'
ORDER BY [total_worker_time] DESC;
有時候使用這種方式並不能獲取存儲過程的執行計划,腳本查詢出來的結果,query_plan字段為NULL值,那么為什么是NULL值呢?這個是因為有一些限制或條件的緣故,官方文檔的解釋如下:
Under the following conditions, no Showplan output is returned in the query_plan column of the returned table for sys.dm_exec_query_plan:
· If the query plan that is specified by using plan_handle has been evicted from the plan cache, the query_plan column of the returned table is null. For example, this condition may occur if there is a time delay between when the plan handle was captured and when it was used with sys.dm_exec_query_plan.
· Some Transact-SQL statements are not cached, such as bulk operation statements or statements containing string literals larger than 8 KB in size. XML Showplans for such statements cannot be retrieved by using sys.dm_exec_query_plan unless the batch is currently executing because they do not exist in the cache.
· If a Transact-SQL batch or stored procedure contains a call to a user-defined function or a call to dynamic SQL, for example using EXEC (string), the compiled XML Showplan for the user-defined function is not included in the table returned by sys.dm_exec_query_plan for the batch or stored procedure. Instead, you must make a separate call to sys.dm_exec_query_plan for the plan handle that corresponds to the user-defined function.
When an ad hoc query uses simple or forced parameterization, the query_plan column will contain only the statement text and not the actual query plan. To return the query plan, call sys.dm_exec_query_plan for the plan handle of the prepared parameterized query. You can determine whether the query was parameterized by referencing the sql column of the sys.syscacheobjects view or the text column of the sys.dm_exec_sql_text dynamic management view.
在以下情況下,sys.dm_exec_query_plan的返回表的query_plan列為空值(query_plan列中未返回Showplan輸出):
· 通過使用plan_handle查詢指定的查詢計划(query plan),如果plan_handle已從計划緩存中踢出(逐出),返回的表的query_plan列為null。 例如,如果在捕獲計划句柄與將其與sys.dm_exec_query_plan一起使用之間存在時間延遲,則可能會出現這種情況。
· 有些Transact-SQL語句不會cached,例如大容量操作語句(bulk operation statements)或包含大於8 KB的字符串大小的SQL語句。無法使用sys.dm_exec_query_plan檢索此類語句的XML Showplans,除非批處理當前正在執行,因為它們不存在於緩存中。
· 如果Transact-SQL批處理或存儲的過程包含對用戶定義函數的調用或執行動態SQL,例如使用 EXEC (字符串),則用戶定義函數的已編譯XML Showplan不包含在返回的表中通過sys.dm_exec_query_plan獲取批處理或存儲過程。相反,您必須單獨調用sys.dm_exec_query_plan以獲取與用戶定義函數對應的計划句柄。
當即席查詢使用簡單或強制參數化時,query_plan列將包含僅語句文本,而不是實際查詢計划。 若要返回查詢計划,請調用sys.dm_exec_query_plan准備參數化查詢的計划句柄。 您可以確定查詢是否已參數化通過引用sql的列sys.syscacheobjects視圖或文本列sys.dm_exec_sql_text動態管理視圖。
注意:sys.dm_exec_query_plan返回的是實際執行計划。
2:使用SET SHOWPLAN_ALL ON 和SET SHOWPLAN_XML ON獲取存儲過程的執行計划。
如下所示,在AdventureWorks2014數據庫中,查看存儲過程[dbo].[uspGetEmployeeManagers] 的執行計划
SET SHOWPLAN_ALL ON
GO
SET FMTONLY ON
GO
EXEC dbo].[uspGetEmployeeManagers] 242;
GO
SET FMTONLY OFF
GO
SET SHOWPLAN_ALL OFF
GO
SET SHOWPLAN_ALL ON
GO
EXEC [dbo].[uspGetEmployeeManagers] 242;
GO
SET SHOWPLAN_ALL OFF;
GO
SET SHOWPLAN_XML ON
GO
EXEC [dbo].[uspGetEmployeeManagers] 242;
GO
SET SHOWPLAN_XML OFF;
GO
這種方式獲取的是存儲過程的預估執行計划,並不是實際執行計划,而且這種方式不會執行存儲過程。另外,如果存儲過程中存在臨時表,那么就會出錯。出現類似下面這樣的錯誤:
Msg 208, Level 16, State 0, Procedure IntegrityTesting, Line 57
Invalid object name '#DbToCheck'.
3:SSMS中選中執行存儲過程的腳本后,使用快捷方式CTRL+L或勾選“Display Estimated Execution Plan”獲取存儲過程的執行計划。
這種方式其實只是方式2(命令方式)的圖形界面操作而已。所以此處不做展開
4:SSMS中使用快捷方式CTRL+M或勾選“Include Actual Execution plan”后,執行存儲過程后,就能看到實際執行計划
這種方式看到的是存儲過程的實際執行計划,但是很多時候,這種方式適用於開發、測試環境,但是我們在生產環境診斷問題時,這種方式並不適用。因為有時候存儲過程會有DML操作,會修改數據。
5:SET STATISTICS XML 或SET STATISTICS PROFILE查看實際執行計划。
SET STATISTICS XML ON
GO
EXEC [dbo].[uspGetEmployeeManagers] 242;
GO
SET STATISTICS XML OFF;
GO
或
SET STATISTICS PROFILE ON
GO
EXEC [dbo].[uspGetEmployeeManagers] 242;
GO
SET STATISTICS PROFILE OFF;
GO
這個方式會執行存儲過程。所以弊端上面也說過,有時候生產環境並不適用。
7:適用SQL Server Profile跟蹤獲取存儲過程的實際執行計划
1: 打開SQL Server Profiler工具
2:在文件菜單,選擇開啟一個新的跟蹤,跟蹤對應的數據庫服務器
3:“Events Section”選項卡中,"Use the template“選擇“blank”
4: 然后在Performance節點,勾選相關字段。“Column Filter"設置過濾選項。例如只跟蹤捕獲某個數據庫的某個存儲過程的執行計划。如下例子所示
總結:我們可以使用上面方式獲取存儲過程的預估執行計划或實際執行計划。各有利弊,主要看使用場景,如果在生產環境診斷問題,優化SQL時,建議使用方式1 或 SQL Server Profiler, 因為有時候預估的執行計划,可能跟實際執行計划有出入,以及參數嗅探問題等等。
參考資料: