在Microsoft SQL Server如何获得查询/存储过程的查询执行计划?
当前回答
您也可以通过powershell使用SET STATISTICS XML ON来获得实际的计划。我写它是为了将多语句计划合并为一个计划;
########## BEGIN : SCRIPT VARIABLES #####################
[string]$server = '.\MySQLServer'
[string]$database = 'MyDatabase'
[string]$sqlCommand = 'EXEC sp_ExampleSproc'
[string]$XMLOutputFileName = 'sp_ExampleSproc'
[string]$XMLOutputPath = 'C:\SQLDumps\ActualPlans\'
########## END : SCRIPT VARIABLES #####################
#Set up connection
$connectionString = "Persist Security Info=False;Integrated Security=true;Connection Timeout=0;Initial Catalog=$database;Server=$server"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
#Set up commands
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$command.CommandTimeout = 0
$commandXMLActPlanOn = new-object system.data.sqlclient.sqlcommand("SET STATISTICS XML ON",$connection)
$commandXMLActPlanOff = new-object system.data.sqlclient.sqlcommand("SET STATISTICS XML OFF",$connection)
$connection.Open()
#Enable session XML plan
$result = $commandXMLActPlanOn.ExecuteNonQuery()
#Execute SP and return resultsets into a dataset
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
#Set up output file name and path
[string]$fileNameDateStamp = get-date -f yyyyMMdd_HHmmss
[string]$XMLOutputFilePath = "$XMLOutputPath$XMLOutputFileName`_$fileNameDateStamp.sqlplan"
#Pull XML plans out of dataset and merge into one multi-statement plan
[int]$cntr = 1
ForEach($table in $dataset.Tables)
{
if($table.Columns[0].ColumnName -eq "Microsoft SQL Server 2005 XML Showplan")
{
[string]$fullXMLPlan = $Table.rows[0]."Microsoft SQL Server 2005 XML Showplan"
if($cntr -eq 1)
{
[regex]$rx = "\<ShowPlanXML xmlns\=.{1,}\<Statements\>"
[string]$startXMLPlan = $rx.Match($fullXMLPlan).Value
[regex]$rx = "\<\/Statements\>.{1,}\<\/ShowPlanXML\>"
[string]$endXMLPlan = $rx.Match($fullXMLPlan).Value
$startXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
}
[regex]$rx = "\<StmtSimple.{1,}\<\/StmtSimple\>"
[string]$bodyXMLPlan = $rx.Match($fullXMLPlan).Value
$bodyXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
$cntr += 1
}
}
$endXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
#Disable session XML plan
$result = $commandXMLActPlanOff.ExecuteNonQuery()
$connection.Close()
其他回答
除了前面回答中描述的方法外,您还可以使用免费的执行计划查看器和查询优化工具ApexSQL plan(我最近碰到了它)。
您可以将ApexSQL计划安装并集成到SQL Server Management Studio中,因此可以直接从SSMS查看执行计划。
在“ApexSQL计划”中查看估计的执行计划
单击SSMS中的新建查询按钮,并将查询文本粘贴到查询文本窗口中。右击并从上下文菜单中选择“Display Estimated Execution Plan”选项。
执行计划图将显示在结果部分的execution plan选项卡中。接下来右键单击执行计划,在上下文菜单中选择“在ApexSQL计划中打开”选项。
预估执行计划将在ApexSQL plan中打开,可以对其进行分析以进行查询优化。
在“ApexSQL计划”中查看实际执行计划
要查看查询的实际执行计划,从前面提到的第二步继续,但是现在,一旦估计计划显示出来,在ApexSQL计划的主带栏中单击“实际”按钮。
一旦点击“实际”按钮,实际执行计划将显示详细的成本参数预览以及其他执行计划数据。
点击此链接可以找到有关查看执行计划的更多信息。
有许多获得执行计划的方法,使用哪一种取决于你的情况。通常你可以使用SQL Server Management Studio来获得一个计划,但是如果由于某种原因你不能在SQL Server Management Studio中运行你的查询,那么你可能会发现通过SQL Server Profiler或检查计划缓存来获得一个计划是有帮助的。
方法1 -使用SQL Server Management Studio
SQL Server提供了一些简洁的功能,可以很容易地捕获执行计划,只需确保选中“包含实际执行计划”菜单项(在“查询”菜单下找到),并正常运行查询即可。
如果你试图获取存储过程中语句的执行计划,那么你应该执行存储过程,如下所示:
exec p_Example 42
当您的查询完成时,您应该在结果窗格中看到一个名为“执行计划”的额外选项卡。如果您运行了许多语句,那么您可能会在这个选项卡中看到许多计划。
从这里,您可以在SQL Server Management Studio中查看执行计划,或者右键单击计划并选择“另存执行计划为…”,将计划保存为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
这些是连接选项,因此每个连接只需要运行一次。从这一点开始,所有运行的语句都将附带一个附加的结果集,其中包含所需格式的执行计划-只需像正常情况下那样运行查询就可以看到计划。
一旦你完成了,你可以用下面的语句关闭这个选项:
SET <<option>> OFF
执行计划格式的比较
除非您有强烈的偏好,否则我建议使用STATISTICS XML选项。该选项相当于SQL Server Management Studio中的“包含实际执行计划”选项,以最方便的格式提供最多的信息。
SHOWPLAN_TEXT - Displays a basic text based estimated execution plan, without executing the query SHOWPLAN_ALL - Displays a text based estimated execution plan with cost estimations, without executing the query SHOWPLAN_XML - Displays an XML based estimated execution plan with cost estimations, without executing the query. This is equivalent to the "Display Estimated Execution Plan..." option in SQL Server Management Studio. STATISTICS PROFILE - Executes the query and displays a text based actual execution plan. STATISTICS XML - Executes the query and displays an XML based actual execution plan. This is equivalent to the "Include Actual Execution Plan" option in SQL Server Management Studio.
方法3 -使用SQL Server分析器
如果你不能直接运行你的查询(或者你的查询在你直接执行它的时候运行得不慢——记住我们想要一个执行糟糕的查询计划),那么你可以使用SQL Server Profiler跟踪来捕获一个计划。其思想是在捕获“Showplan”事件之一的跟踪运行时运行查询。
Note that depending on load you can use this method on a production environment, however you should obviously use caution. The SQL Server profiling mechanisms are designed to minimize impact on the database but this doesn't mean that there won't be any performance impact. You may also have problems filtering and identifying the correct plan in your trace if your database is under heavy use. You should obviously check with your DBA to see if they are happy with you doing this on their precious database!
Open SQL Server Profiler and create a new trace connecting to the desired database against which you wish to record the trace. Under the "Events Selection" tab check "Show all events", check the "Performance" -> "Showplan XML" row and run the trace. While the trace is running, do whatever it is you need to do to get the slow running query to run. Wait for the query to complete and stop the trace. To save the trace right click on the plan xml in SQL Server Profiler and select "Extract event data..." to save the plan to file in XML format.
你得到的计划相当于SQL Server Management Studio中的“包含实际执行计划”选项。
方法4 -检查查询缓存
如果不能直接运行查询,也不能捕获分析器跟踪,那么仍然可以通过检查SQL查询计划缓存来获得估计的计划。
我们通过查询SQL Server dmv来检查计划缓存。下面是一个基本查询,它将列出所有缓存的查询计划(以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,在新窗口中打开计划-右键单击并选择“Save execution plan as…”,以XML格式将计划保存为文件。
注:
因为涉及到很多因素(从表和索引模式到存储的数据和表统计信息),所以应该始终尝试从您感兴趣的数据库(通常是遇到性能问题的数据库)获取执行计划。
无法捕获加密存储过程的执行计划。
“实际”和“估计”执行计划
实际执行计划是指SQL Server实际运行查询的计划,而预估执行计划则是指SQL Server在不执行查询的情况下会做什么。尽管逻辑上是等价的,但实际的执行计划更有用,因为它包含执行查询时实际发生的情况的额外细节和统计信息。这在诊断SQL server估计错误的问题(例如统计数据过期)时非常重要。
预估和实际执行计划重审
如何解释查询执行计划?
这是一个值得写一本(免费)书的主题。
参见:
执行计划基础知识 SHOWPLAN权限和Transact-SQL批处理 SQL Server 2008 -使用查询哈希和查询计划哈希 分析SQL Server计划缓存
您也可以通过powershell使用SET STATISTICS XML ON来获得实际的计划。我写它是为了将多语句计划合并为一个计划;
########## BEGIN : SCRIPT VARIABLES #####################
[string]$server = '.\MySQLServer'
[string]$database = 'MyDatabase'
[string]$sqlCommand = 'EXEC sp_ExampleSproc'
[string]$XMLOutputFileName = 'sp_ExampleSproc'
[string]$XMLOutputPath = 'C:\SQLDumps\ActualPlans\'
########## END : SCRIPT VARIABLES #####################
#Set up connection
$connectionString = "Persist Security Info=False;Integrated Security=true;Connection Timeout=0;Initial Catalog=$database;Server=$server"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
#Set up commands
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$command.CommandTimeout = 0
$commandXMLActPlanOn = new-object system.data.sqlclient.sqlcommand("SET STATISTICS XML ON",$connection)
$commandXMLActPlanOff = new-object system.data.sqlclient.sqlcommand("SET STATISTICS XML OFF",$connection)
$connection.Open()
#Enable session XML plan
$result = $commandXMLActPlanOn.ExecuteNonQuery()
#Execute SP and return resultsets into a dataset
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
#Set up output file name and path
[string]$fileNameDateStamp = get-date -f yyyyMMdd_HHmmss
[string]$XMLOutputFilePath = "$XMLOutputPath$XMLOutputFileName`_$fileNameDateStamp.sqlplan"
#Pull XML plans out of dataset and merge into one multi-statement plan
[int]$cntr = 1
ForEach($table in $dataset.Tables)
{
if($table.Columns[0].ColumnName -eq "Microsoft SQL Server 2005 XML Showplan")
{
[string]$fullXMLPlan = $Table.rows[0]."Microsoft SQL Server 2005 XML Showplan"
if($cntr -eq 1)
{
[regex]$rx = "\<ShowPlanXML xmlns\=.{1,}\<Statements\>"
[string]$startXMLPlan = $rx.Match($fullXMLPlan).Value
[regex]$rx = "\<\/Statements\>.{1,}\<\/ShowPlanXML\>"
[string]$endXMLPlan = $rx.Match($fullXMLPlan).Value
$startXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
}
[regex]$rx = "\<StmtSimple.{1,}\<\/StmtSimple\>"
[string]$bodyXMLPlan = $rx.Match($fullXMLPlan).Value
$bodyXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
$cntr += 1
}
}
$endXMLPlan | out-file -Append -FilePath $XMLOutputFilePath
#Disable session XML plan
$result = $commandXMLActPlanOff.ExecuteNonQuery()
$connection.Close()
除了已经发布的综合答案之外,有时能够以编程方式访问执行计划以提取信息也是有用的。下面是示例代码。
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
停止捕获定义
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
在SQL Server Management Studio:
“Ctrl + M”将生成实际执行计划
“Ctrl + L”将生成估计执行计划
“Shift + Alt + S”为客户端统计
“Ctrl + Alt + P”用于SQL Server Profiler中的跟踪查询。
推荐文章
- 我如何在T-SQL用逗号格式化一个数字?
- LEFT OUTER JOIN如何返回比左表中存在的记录更多的记录?
- 如何用SQL语句计算百分比
- 数组与列表的性能
- 使用私有静态方法的优点
- Postgres唯一约束与索引
- SQL Server动态PIVOT查询?
- 如何等待2秒?
- SQL Server: CROSS JOIN和FULL OUTER JOIN的区别是什么?
- MySQL对重复键更新在一个查询中插入多行
- 如何在内存中获取对象大小?
- varchar和nvarchar SQL Server数据类型之间的主要性能差异是什么?
- 向现有表添加主键
- 为什么512x512矩阵的转置比513x513矩阵的转置慢得多?
- 使用电子邮件地址为主键?