如何获得查询执行计划?

在Microsoft SQL Server中,如何获取查询/存储过程的查询执行计划?

有很多获得执行计划的方法,哪一个方法取决于你的情况。 通常你可以使用SQL Server Management Studio来获得一个计划,但是如果由于某种原因你不能在SQL Server Management Studio中运行你的查询,那么你可能会发现能够通过SQL Server Profiler获得一个计划或者通过检查计划caching。

方法1 – 使用SQL Server Management Studio

SQL Server附带了一些简单的function,使得捕获执行计划变得非常简单,只需确保“包括实际执行计划”菜单项(在“查询”菜单下find)被打勾并正常运行查询。

包括“操作执行计划”菜单项

如果您正在尝试获取存储过程中语句的执行计划,那么您应该执行存储过程,如下所示:

exec p_Example 42 

当您的查询完成后,您应在结果窗格中看到名为“执行计划”的额外选项卡。 如果您运行了很多语句,则可能会在此选项卡中看到许多计划。

执行计划的屏幕截图

从这里您可以检查SQL Server Management Studio中的执行计划,或者右键单击计划并select“将执行计划另存为…”将计划保存为XML格式的文件。

方法2 – 使用SHOWPLAN选项

此方法与方法1非常相似(实际上这是SQL Server Management Studio在内部执行的操作),但是为了完整,或者您没有SQL Server Management Studio可用,我已经包含了它。

在运行查询之前,运行以下语句之一。 声明必须是批处理中的唯一声明,即不能同时执行另一个声明:

 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 

这些是连接选项,所以你只需要每个连接运行一次。 从这一点开始,所有运行的语句都将以包含您的执行计划的额外结果集作为期望的格式运行 – 只需像平常一样查看计划即可运行查询。

完成后,可以使用以下语句closures该选项:

 SET <<option>> OFF 

执行计划格式的比较

除非您有强烈的偏好,否则我的build议是使用STATISTICS XML选项。 此选项等同于SQL Server Management Studio中的“包括实际执行计划”选项,并以最方便的格式提供大多数信息。

  • SHOWPLAN_TEXT – 显示基于文本的估计执行计划,而不执行查询
  • SHOWPLAN_ALL – 显示一个基于文本的估计执行计划与成本估算,而不执行查询
  • SHOWPLAN_XML – 在不执行查询的情况下,显示基于XML的估算执行计划,并进行成本估算。 这相当于SQL Server Management Studio中的“显示估计执行计划…”选项。
  • STATISTICS PROFILE – 执行查询并显示基于文本的实际执行计划。
  • STATISTICS XML – 执行查询并显示基于XML的实际执行计划。 这相当于SQL Server Management Studio中的“包括实际执行计划”选项。

方法3 – 使用SQL Server Profiler

如果无法直接运行查询(或者直接执行查询时运行速度不够快,请记住我们希望计划查询性能很差),那么可以使用SQL Server Profiler跟踪捕获计划。 这个想法是运行您的查询,而正在捕获“Showplan”事件之一的轨迹正在运行。

请注意,根据负载情况,您可以在生产环境中使用此方法,但显然要谨慎使用。 SQL Server分析机制旨在最大限度地减less对数据库的影响,但这并不意味着不会有任何性能影响。 如果您的数据库被大量使用,您也可能在筛选和确定跟踪中的正确计划时遇到问题。 你应该明确地检查你的DBA,看看他们是否对你在他们宝贵的数据库上做这件事感到高兴!

  1. 打开SQL Server Profiler并创build一个新的跟踪,连接到您希望logging跟踪的所需数据库。
  2. 在“事件select”选项卡下,选中“显示所有事件”,检查“性能” – >“显示计划XML”行并运行跟踪。
  3. 跟踪正在运行时,请执行任何您需要执行的操作,以使运行缓慢的查询运行。
  4. 等待查询完成并停止跟踪。
  5. 要保存跟踪在SQL Server Profiler的计划xml上右键单击并select“提取事件数据…”将计划以XML格式保存到文件。

您所得到的计划等同于SQL Server Management Studio中的“包括实际执行计划”选项。

方法4 – 检查查询caching

如果您无法直接运行查询,也无法捕获探查器跟踪,那么您仍然可以通过检查SQL查询计划caching来获取估计的计划。

我们通过查询SQL Server DMV来检查计划caching。 以下是一个基本查询,它将列出所有caching的查询计划(如xml)及其SQL文本。 在大多数数据库中,您还需要添加额外的过滤子句来将结果过滤到您感兴趣的计划中。

 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以在新窗口中打开计划 – 右键单击​​并select“将执行计划另存为…”将计划以XML格式保存到文件。

笔记:

因为涉及的因素太多(从表和索引模式到存储的数据以及表统计信息),您应该始终尝试从您感兴趣的数据库获取执行计划(通常是遇到性能问题的计划问题)。

您无法捕获encryption存储过程的执行计划。

“实际”与“估计”执行计划

一个实际的执行计划是SQL Server实际运行查询的计划,而一个估计的执行计划SQL Server 在不执行查询的情况下计算出它将执行的操作。 虽然在逻辑上是等价的,但实际执行计划更有用,因为它包含有关执行查询时实际发生的事情的更多详细信息和统计信息。 在诊断SQL Server估算失败的问题时(例如统计数据过期时),这是非常重要的。

  • 预计和实际执行计划重新审视

如何解释查询执行计划?

对于一本(免费) 书本身来说,这是一个足够的话题。

也可以看看:

  • 执行计划基础
  • SHOWPLAN权限和Transact-SQL批处理
  • SQL Server 2008 – 使用查询散列和查询计划散列
  • 分析SQL Server计划caching

除了已经发布的综合答案之外,有时能够以编程方式访问执行计划来提取信息是有用的。 示例代码如下。

 DECLARE @TraceID INT EXEC StartCapture @@SPID, @TraceID OUTPUT EXEC sp_help 'sys.objects' /*<-- Call your stored proc of interest here.*/ EXEC StopCapture @TraceID 

示例StartCapture定义

 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 

示例StopCapture定义

 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 

假设您正在使用Microsoft SQL Server Management Studio

  • 对于估计查询计划,您可以按Ctrl + L或以下button。

在这里输入图像描述

  • 对于实际查询计划 ,您可以在执行查询之前按Ctrl + M或以下button。

在这里输入图像描述

  • 对于实时查询计划 ,(仅在SSMS 2016中)在执行查询之前使用以下button。

在这里输入图像描述

我最喜欢的获取和深入分析查询执行计划的工具是SQL Sentry Plan Explorer 。 执行计划的详细分析和可视化比SSMS更方便,更方便,更全面。

下面是一个示例屏幕截图,供您了解该工具提供的function:

SQL Sentry计划资源管理器窗口屏幕截图

这只是工具中可用的视图之一。 注意到应用程序窗口底部的一组选项卡,它可以让您获得不同types的执行计划表示和有用的附加信息。

另外,我没有注意到它的免费版本的任何限制,以防止每天使用它或迫使你最终购买专业版。 所以,如果你喜欢坚持免费版,没有任何东西禁止你这样做。

更新:(感谢马丁史密斯 )计划资源pipe理器现在是免费的! 有关详细信息,请参见http://www.sqlsentry.com/products/plan-explorer/sql-server-query-view

除了在前面的答案中描述的方法,您还可以使用免费的执行计划查看器和查询优化工具ApexSQL计划 (我最近碰到)。

您可以安装并将ApexSQL Plan集成到SQL Server Management Studio中,从而可以直接从SSMS查看执行计划。

在ApexSQL计划中查看估计的执行计划

  1. 单击SSMS中的“ 新build查询”button,并将查询文本粘贴到查询文本窗口中。 右键单击并从上下文菜单中select“显示估计执行计划”选项。

SSMS中的新查询按钮

  1. 执行计划图将显示结果部分中的执行计划选项卡。 然后右键单击执行计划并在上下文菜单中select“在ApexSQL计划中打开”选项。

执行计划

  1. 预计的执行计划将在ApexSQL计划中打开,并且可以分析查询优化。

预计的执行计划

查看ApexSQL计划中的实际执行计划

要查看查询的实际执行计划,请从前面提到的第二步继续,但是现在,一旦显示了预计计划,单击ApexSQL计划中主function栏上的“实际”button。

单击主功能栏上的“实际”按钮

一旦点击“实际”button,实际执行计划将显示成本参数的详细预览以及其他执行计划数据。

实际的执行计划

关于查看执行计划的更多信息可以通过以下链接find。

可以通过query_post_execution_showplan事件从扩展事件会话中获取查询计划。 这是一个示例XEvent会话:

 /* 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 

创build会话后,(在SSMS中)转到对象资源pipe理器并深入pipe理| 扩展事件| 会话。 右键单击“GetExecutionPlan”会话并启动它。 再次右键单击并select“观看实时数据”。

接下来,打开一个新的查询窗口并运行一个或多个查询。 这里是一个AdventureWorks的:

 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”选项卡中看到一些结果。 单击网格中的一个query_post_execution_showplan事件,然后单击网格下方的“查询计划”选项卡。 它应该看起来类似于这个:

在这里输入图像描述

编辑 :XEvent代码和屏幕截图是从SQL / SSMS 2012 w / SP2生成的。 如果您使用SQL 2008 / R2,则可以调整脚本以使其运行。 但是该版本没有GUI,所以你必须提取showplan XML,将其保存为* .sqlplan文件并在SSMS中打开它。 这很麻烦。 XEvents在SQL 2005或更早版本中不存在。 所以,如果你不在SQL 2012或更高版本,我强烈build议在这里发布的其他答案之一。