How do I obtain a Query Execution Plan?
在Microsoft SQL Server中,如何获取查询/存储过程的查询执行计划?
有许多方法可以获得一个执行计划,使用哪一个将取决于您的情况。通常,您可以使用SQL Server Management Studio获取计划,但是,如果由于某种原因无法在SQL Server Management Studio中运行查询,那么您可能会发现通过SQL Server事件探查器或通过检查计划缓存获取计划很有帮助。好的。方法1-使用SQL Server Management Studio
SQL Server提供了一些简洁的功能,可以很容易地捕获执行计划,只需确保勾选"包括实际执行计划"菜单项(在"查询"菜单下找到)并正常运行查询。好的。
好的。
如果试图获取存储过程中语句的执行计划,则应执行该存储过程,如下所示:好的。
1 | EXEC p_Example 42 |
当您的查询完成时,您应该会在结果窗格中看到一个名为"执行计划"的额外选项卡。如果您运行了许多语句,那么您可能会在该选项卡中看到许多计划。好的。
好的。
从这里可以检查SQL Server Management Studio中的执行计划,或者右键单击该计划并选择"将执行计划另存为…"将该计划保存为XML格式的文件。好的。方法2-使用Showplan选项
此方法与方法1非常相似(实际上,这是SQL Server Management Studio在内部所做的),但是为了完整性,或者如果您没有可用的SQL Server Management Studio,我已经包含了它。好的。
在运行查询之前,请运行以下语句之一。该语句必须是批处理中唯一的语句,即不能同时执行另一个语句:好的。
1 2 3 4 5 | SET SHOWPLAN_TEXT ON SET SHOWPLAN_ALL ON SET SHOWPLAN_XML ON SET STATISTICS PROFILE ON SET STATISTICS XML ON -- The is the recommended option to use |
这些是连接选项,因此每个连接只需要运行一次。从这一点开始,所有运行的语句都将由一个额外的结果集进行比较,该结果集以所需的格式包含您的执行计划——只需像通常看到计划那样运行查询。好的。
完成后,可以使用以下语句关闭此选项:好的。
1 | SET <<option>> OFF |
执行计划格式比较
除非你有强烈的偏好,我的建议是使用
SHOWPLAN_TEXT —显示基于文本的基本估计执行计划,而不执行查询SHOWPLAN_ALL —显示基于文本的估计执行计划和成本估计,而不执行查询SHOWPLAN_XML —显示一个基于XML的估计执行计划,其中包含成本估计,而不执行查询。这相当于SQL Server Management Studio中的"显示估计执行计划…"选项。STATISTICS PROFILE —执行查询并显示基于文本的实际执行计划。STATISTICS XML —执行查询并显示基于XML的实际执行计划。这相当于SQL Server Management Studio中的"包括实际执行计划"选项。
方法3-使用SQL Server事件探查器
如果不能直接运行查询(或者直接执行查询时查询运行速度不慢-请记住,我们希望查询计划执行得不好),则可以使用SQL Server事件探查器跟踪捕获计划。其思想是在捕获"showplan"事件之一的跟踪正在运行时运行查询。好的。
请注意,根据负载的不同,您可以在生产环境中使用此方法,但显然应该谨慎使用。SQL Server分析机制旨在将对数据库的影响降至最低,但这并不意味着不会有任何性能影响。如果数据库大量使用,那么在跟踪中筛选和识别正确的计划也可能会出现问题。显然,您应该与您的DBA一起检查,看看他们是否愿意您在他们宝贵的数据库上这样做!好的。
您得到的计划相当于SQL Server Management Studio中的"包括实际执行计划"选项。好的。方法4-检查查询缓存
如果不能直接运行查询,也无法捕获探查器跟踪,那么仍然可以通过检查SQL查询计划缓存来获取估计的计划。好的。
我们通过查询SQL Server DMV来检查计划缓存。下面是一个基本查询,它将列出所有缓存的查询计划(作为XML)及其SQL文本。在大多数数据库中,您还需要添加额外的筛选子句,以便将结果筛选为您感兴趣的计划。好的。
1 2 3 4 | SELECT UseCounts, Cacheobjtype, Objtype, TEXT, query_plan FROM sys.dm_exec_cached_plans CROSS APPLY sys.dm_exec_sql_text(plan_handle) CROSS APPLY sys.dm_exec_query_plan(plan_handle) |
执行此查询并单击计划XML以在新窗口中打开计划-右键单击并选择"将执行计划另存为…"以XML格式将计划保存到文件中。好的。笔记:
因为涉及的因素太多(从表和索引模式到存储的数据和表统计信息),所以您应该始终尝试从感兴趣的数据库(通常是遇到性能问题的数据库)获取执行计划。好的。
无法捕获加密存储过程的执行计划。好的。"实际"与"估计"执行计划
实际的执行计划是指SQL Server实际运行查询的计划,而估计的执行计划SQL Server在不执行查询的情况下完成它的工作。尽管在逻辑上是等效的,但实际的执行计划更有用,因为它包含有关执行查询时实际发生的事情的附加详细信息和统计信息。在诊断SQL Server估计值已关闭的问题(例如统计数据已过期)时,这一点非常重要。好的。
- 重新评估估计和实际执行计划
如何解释查询执行计划?
这是一个有足够价值的主题(免费)一本书本身。好的。参见:
- 执行计划基础
- ShowPlan权限和Transact-SQL批处理
- SQL Server 2008–使用查询哈希和查询计划哈希
- 分析SQL Server计划缓存
好啊。
除了已经发布的综合答案之外,有时还可以通过编程访问执行计划来提取信息。下面是这方面的示例代码。
1 2 3 4 | DECLARE @TraceID INT EXEC StartCapture @@SPID, @TraceID OUTPUT EXEC sp_help 'sys.objects' /*<-- Call your stored proc of interest here.*/ EXEC StopCapture @TraceID |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CREATE PROCEDURE StartCapture @Spid INT, @TraceID INT OUTPUT AS DECLARE @maxfilesize BIGINT = 5 DECLARE @filepath NVARCHAR(200) = N'C:\trace_' + LEFT(NEWID(),36) EXEC sp_trace_create @TraceID OUTPUT, 0, @filepath, @maxfilesize, NULL EXEC sp_trace_setevent @TraceID, 122, 1, 1 EXEC sp_trace_setevent @TraceID, 122, 22, 1 EXEC sp_trace_setevent @TraceID, 122, 34, 1 EXEC sp_trace_setevent @TraceID, 122, 51, 1 EXEC sp_trace_setevent @TraceID, 122, 12, 1 -- filter for spid EXEC sp_trace_setfilter @TraceID, 12, 0, 0, @Spid -- start the trace EXEC sp_trace_setstatus @TraceID, 1 |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | CREATE PROCEDURE StopCapture @TraceID INT AS WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS SQL), CTE AS (SELECT CAST(TextData AS VARCHAR(MAX)) AS TextData, ObjectID, ObjectName, EventSequence, /*costs accumulate up the tree so the MAX should be the root*/ MAX(EstimatedTotalSubtreeCost) AS EstimatedTotalSubtreeCost FROM fn_trace_getinfo(@TraceID) fn CROSS APPLY fn_trace_gettable(CAST(VALUE AS NVARCHAR(200)), 1) CROSS APPLY (SELECT CAST(TextData AS XML) AS xPlan) x CROSS APPLY (SELECT T.relop.value('@EstimatedTotalSubtreeCost', 'float') AS EstimatedTotalSubtreeCost FROM xPlan.nodes('//sql:RelOp') T(relop)) ca WHERE property = 2 AND TextData IS NOT NULL AND ObjectName NOT IN ( 'StopCapture', 'fn_trace_getinfo' ) GROUP BY CAST(TextData AS VARCHAR(MAX)), ObjectID, ObjectName, EventSequence) SELECT ObjectName, SUM(EstimatedTotalSubtreeCost) AS EstimatedTotalSubtreeCost FROM CTE GROUP BY ObjectID, ObjectName -- Stop the trace EXEC sp_trace_setstatus @TraceID, 0 -- Close and delete the trace EXEC sp_trace_setstatus @TraceID, 2 GO |
Assuming you're using Microsoft SQL Server Management Studio
- 对于估计的查询计划,可以按ctrl+l或以下按钮。
- 对于实际的查询计划,可以按Ctrl键+m或执行查询前的以下按钮。
- 对于实时查询计划(仅限SSMS 2016),请在执行查询之前使用以下按钮。
除了前面答案中描述的方法之外,您还可以使用一个免费的执行计划查看器和查询优化工具apexsql plan(我最近碰到过这个工具)。
您可以安装apexsql计划并将其集成到SQL Server Management Studio中,这样就可以直接从ssms查看执行计划。
查看apexsql计划中的估计执行计划
查看apexsql计划中的实际执行计划
要查看查询的实际执行计划,请从前面提到的第2步继续,但现在,一旦显示了估计计划,请单击apexsql计划中主功能区栏中的"实际"按钮。
单击"实际"按钮后,将显示实际执行计划以及成本参数的详细预览以及其他执行计划数据。
通过以下链接可以找到有关查看执行计划的更多信息。
我最喜欢的获取和深入分析查询执行计划的工具是SQL Sentry Plan Explorer。与SSMS相比,它对执行计划的详细分析和可视化更为用户友好、方便和全面。
下面是一个示例屏幕截图,您可以了解该工具提供的功能:
它只是工具中可用的视图之一。请注意,在应用程序窗口底部有一组选项卡,可以让您获得不同类型的执行计划表示和有用的附加信息。
此外,我还没有注意到它的免费版有任何限制,这些限制会阻止您每天使用它,或者最终迫使您购买Pro版。所以,如果你喜欢坚持使用免费版本,没有什么能阻止你这样做。
更新:(感谢Martin Smith)Plan Explorer现在是免费的!有关详细信息,请参阅http://www.sqlsentry.com/products/plan-explorer/sql-server-query-view。
查询计划可以通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /* Generated via"Query Detail Tracking" template. */ CREATE EVENT SESSION [GetExecutionPlan] ON SERVER ADD EVENT sqlserver.query_post_execution_showplan( ACTION(package0.event_sequence,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack)), /* Remove any of the following events (or include additional events) as desired. */ ADD EVENT sqlserver.error_reported( ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))), ADD EVENT sqlserver.module_end(SET collect_statement=(1) ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))), ADD EVENT sqlserver.rpc_completed( ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))), ADD EVENT sqlserver.sp_statement_completed(SET collect_object_name=(1) ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))), ADD EVENT sqlserver.sql_batch_completed( ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))), ADD EVENT sqlserver.sql_statement_completed( ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.database_id,sqlserver.plan_handle,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack) WHERE ([package0].[greater_than_uint64]([sqlserver].[database_id],(4)) AND [package0].[equal_boolean]([sqlserver].[is_system],(0)))) ADD TARGET package0.ring_buffer WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF) GO |
创建会话后(在SSMS中),转到对象资源管理器,深入研究管理扩展事件会话。右键单击"GetExecutionPlan"会话并启动它。再次右键单击并选择"观看实时数据"。
接下来,打开一个新的查询窗口并运行一个或多个查询。以下是冒险作品:
1 2 3 4 5 6 7 8 9 10 11 | USE AdventureWorks; GO SELECT p.Name AS ProductName, NonDiscountSales = (OrderQty * UnitPrice), Discounts = ((OrderQty * UnitPrice) * UnitPriceDiscount) FROM Production.Product AS p INNER JOIN Sales.SalesOrderDetail AS sod ON p.ProductID = sod.ProductID ORDER BY ProductName DESC; GO |
一两分钟后,您将在"GetExecutionPlan:Live Data"选项卡中看到一些结果。单击网格中的一个查询"执行后"显示计划事件,然后单击网格下面的"查询计划"选项卡。它应该看起来像这样:
编辑:XEvent代码和屏幕截图是从SQL/SSMS 2012 w/sp2生成的。如果您使用的是SQL 2008/R2,则可以调整脚本使其运行。但该版本没有GUI,因此您必须提取ShowPlanXML,将其保存为*.sqlplan文件并在SSMS中打开。那太麻烦了。XEvents在SQL 2005或更早版本中不存在。因此,如果您不在SQL 2012或更高版本上,我强烈建议您选择这里发布的其他答案之一。
从SQL Server 2016+开始,引入了查询存储功能来监视性能。它提供了对查询计划选择和性能的深入了解。它不是跟踪或扩展事件的完全替代,但是随着它从一个版本发展到另一个版本,我们可能会在未来的SQL Server版本中得到一个功能完整的查询存储。查询存储的主流
- 计划存储-保存执行计划信息
- 运行时统计存储-持久化执行统计信息
- 查询等待统计信息存储-保持等待统计信息。
启用查询存储:查询存储在服务器上的数据库级别工作。
- 默认情况下,新数据库的查询存储不活动。
- 不能为master或
tempdb 数据库启用查询存储。 - 有效二甲基亚砜
sys.database_query_store_options (Transact-SQL)
在查询存储中收集信息:我们使用查询存储DMV(数据管理视图)从三个存储中收集所有可用信息。
查询计划存储:保存执行计划信息,并负责捕获与查询编译相关的所有信息。
sys.query_store_query (Transact-SQL)
sys.query_store_plan (Transact-SQL)
sys.query_store_query_text (Transact-SQL)运行时状态存储:保存执行统计信息,它可能是最频繁更新的存储。这些统计数据表示查询执行数据。
sys.query_store_runtime_stats (Transact-SQL)查询等待状态存储:保存和捕获等待统计信息。
sys.query_store_wait_stats (Transact-SQL)
注意:查询等待状态存储仅在SQL Server 2017中可用+
除了前面所说的,还有一件重要的事情要知道。
查询计划通常过于复杂,无法用内置XML列类型表示,该列类型的嵌套元素限制为127级。这就是sys.dm eu exec_query_plan可能返回
1 2 3 4 5 6 | SELECT p.query_plan FROM sys.dm_exec_requests AS r OUTER APPLY sys.dm_exec_text_query_plan( r.plan_handle, r.statement_start_offset, r.statement_end_offset) AS p |
但是,与XML列相比,结果表中的文本列并不十分方便。为了能够单击要在单独的选项卡中作为图表打开的结果,而不必将其内容保存到文件中,您可以使用一个小技巧(记住,您不能只使用EDOCX1[1]),尽管这只适用于一行:
1 2 3 4 5 6 7 8 | SELECT Tag = 1, Parent = NULL, [ShowPlanXML!1!!XMLTEXT] = query_plan FROM sys.dm_exec_text_query_plan( -- set these variables or copy values -- from the results of the above query @plan_handle, @statement_start_offset, @statement_end_offset) FOR XML EXPLICIT |
与SQL Server Management Studio(已经解释过)一样,也可以像这里解释的那样使用数据报。
Right-click an SQL statement, and select Explain plan. In the Output pane, click Plan. By default, you see the tree representation of the query. To see the
query plan, click the Show Visualization icon, or press
Ctrl+Shift+Alt+U