【问题标题】:How do I obtain a Query Execution Plan in SQL Server?如何在 SQL Server 中获取查询执行计划?
【发布时间】:2011-11-13 15:41:28
【问题描述】:

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

【问题讨论】:

  • 如何在 SQL Server Management Studio 中关闭执行计划选项卡?
  • @Paul 你可以按 Ctrl + R 。它关闭了整个结果部分 - 包括消息和执行计划。

标签: sql sql-server performance tsql sql-execution-plan


【解决方案1】:

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

这里是一个示例屏幕截图,让您了解该工具提供的功能:

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

此外,我没有注意到它的免费版有任何限制,会阻止您每天使用它或最终迫使您购买专业版。因此,如果您更喜欢使用免费版,没有什么可以阻止您这样做。

【讨论】:

  • 谁在说第三方工具?
  • @basher:OP 没有限制使用 MS 工具或其他方式的方法。那么,是什么让您认为涉及第三方工具的答案不合适呢?
  • 当没有人提到第三方工具时,您只是在开玩笑说您的答案开头Speaking of third-party tools
  • @basher:哦,不错的收获!谢谢!我已经重写了我的答案。如果您愿意,请随时提供反馈和/或投票。
  • 顺便说一句,现在只有一个版本sqlsentry.com/products/plan-explorer/sql-server-query-view
【解决方案2】:

在 SQL Server Management Studio 中:

“Ctrl + M”将生成实际执行计划

“Ctrl + L”会生成预估执行计划

“Shift + Alt + S”用于客户统计

“Ctrl + Alt + P”用于在 SQL Server Profiler 中跟踪查询。

【讨论】:

    【解决方案3】:

    预计执行计划

    优化器在不运行 SQL 查询的情况下生成估计的执行计划。

    为了获得估计的执行计划,您需要在执行查询之前启用SHOWPLAN_ALL设置。

    SET SHOWPLAN_ALL ON
    

    现在,当执行以下 SQL 查询时:

    SELECT p.id
    FROM post p
    WHERE EXISTS (
      SELECT 1
      FROM post_comment pc
      WHERE
        pc.post_id = p.id AND
        pc.review = 'Bingo'
    )
    ORDER BY p.title
    OFFSET 20 ROWS
    FETCH NEXT 10 ROWS ONLY
    

    SQL Server 将生成以下估计的执行计划:

    | NodeId | Parent | LogicalOp            | EstimateRows | EstimateIO  | EstimateCPU | AvgRowSize | TotalSubtreeCost | EstimateExecutions |
    |--------|--------|----------------------|--------------|-------------|-------------|------------|------------------|--------------------|
    | 1      | 0      | NULL                 | 10           | NULL        | NULL        | NULL       | 0.03374284       | NULL               |
    | 2      | 1      | Top                  | 10           | 0           | 3.00E-06    | 15         | 0.03374284       | 1                  |
    | 4      | 2      | Distinct Sort        | 30           | 0.01126126  | 0.000504114 | 146        | 0.03373984       | 1                  |
    | 5      | 4      | Inner Join           | 46.698       | 0           | 0.00017974  | 146        | 0.02197446       | 1                  |
    | 6      | 5      | Clustered Index Scan | 43           | 0.004606482 | 0.0007543   | 31         | 0.005360782      | 1                  |
    | 7      | 5      | Clustered Index Seek | 1            | 0.003125    | 0.0001581   | 146        | 0.0161733        | 43                 |
    

    运行我们感兴趣的查询后获取估计的执行计划,您需要禁用SHOWPLAN_ALL,否则当前数据库会话将只生成估计的执行计划而不执行提供的SQL查询。

    SET SHOWPLAN_ALL OFF
    

    SQL Server Management Studio 预估方案

    在 SQL Server Management Studio 应用程序中,您可以通过点击CTRL+L 快捷键轻松获取任何 SQL 查询的估计执行计划。

    实际执行计划

    实际的 SQL 执行计划是由优化器在运行 SQL 查询时生成的。如果数据库表统计准确,则实际计划与估计的计划应该不会有太大差异。

    要在 SQL Server 上获得实际的执行计划,您需要启用 STATISTICS IO, TIME, PROFILE 设置,如以下 SQL 命令所示:

    SET STATISTICS IO, TIME, PROFILE ON
    

    现在,当运行上一个查询时,SQL Server 将生成以下执行计划:

    | Rows | Executes | NodeId | Parent | LogicalOp            | EstimateRows | EstimateIO  | EstimateCPU | AvgRowSize | TotalSubtreeCost |
    |------|----------|--------|--------|----------------------|--------------|-------------|-------------|------------|------------------|
    | 10   | 1        | 1      | 0      | NULL                 | 10           | NULL        | NULL        | NULL       | 0.03338978       |
    | 10   | 1        | 2      | 1      | Top                  | 1.00E+01     | 0           | 3.00E-06    | 15         | 0.03338978       |
    | 30   | 1        | 4      | 2      | Distinct Sort        | 30           | 0.01126126  | 0.000478783 | 146        | 0.03338679       |
    | 41   | 1        | 5      | 4      | Inner Join           | 44.362       | 0           | 0.00017138  | 146        | 0.02164674       |
    | 41   | 1        | 6      | 5      | Clustered Index Scan | 41           | 0.004606482 | 0.0007521   | 31         | 0.005358581      |
    | 41   | 41       | 7      | 5      | Clustered Index Seek | 1            | 0.003125    | 0.0001581   | 146        | 0.0158571        |
     
    SQL Server parse and compile time:
       CPU time = 8 ms, elapsed time = 8 ms.
     
    (10 row(s) affected)
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'post'. Scan count 0, logical reads 116, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'post_comment'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
     
    (6 row(s) affected)
     
    SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 1 ms.
    

    运行查询后我们有兴趣获取实际的执行计划,您需要像这样禁用STATISTICS IO, TIME, PROFILE ON 设置:

    SET STATISTICS IO, TIME, PROFILE OFF
    

    SQL Server Management Studio 实际方案

    在 SQL Server Management Studio 应用程序中,您可以通过点击CTRL+M 快捷键轻松获取任何 SQL 查询的估计执行计划。

    【讨论】:

      【解决方案4】:

      您也可以通过 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()
      

      【讨论】:

        【解决方案5】:

        解释执行计划可能非常详细并且需要相当长的阅读时间,但总的来说,如果您在查询之前使用“解释”,它应该会给您很多信息,包括哪些部分首先执行等等。 如果你想了解更多关于这个的细节,我编译了一个关于这个的小博客,它也为你指出了正确的参考。 https://medium.com/swlh/jetbrains-datagrip-explain-plan-ac406772c470

        【讨论】:

          【解决方案6】:

          与 SQL Server Management Studio(已解释)一样,Datagrip 也可以使用,如 here 所述。

          1. 右键单击 SQL 语句,然后选择解释计划。
          2. 在“输出”窗格中,单击“计划”。
          3. 默认情况下,您会看到查询的树形表示。要查看 查询计划,单击“显示可视化”图标,或按 Ctrl+Shift+Alt+U

          【讨论】:

            【解决方案7】:

            除了前面所说的一切之外,还有一件重要的事情要知道。

            查询计划通常过于复杂,无法由内置的 XML 列类型表示,该列类型具有 127 级的限制嵌套元素。这就是为什么sys.dm_exec_query_plan 可能会返回NULL 甚至在早期的MS SQL 版本中抛出错误的原因之一,因此通常使用sys.dm_exec_text_query_plan 会更安全。后者还有一个有用的奖励功能,即选择针对特定语句的计划而不是整个批次。以下是您如何使用它来查看当前正在运行的语句的计划:

            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 列相比不是很方便。为了能够单击以图表形式在单独选项卡中打开的结果,而不必将其内容保存到文件中,您可以使用一个小技巧(记住您不能只使用CAST(... AS XML)),尽管这只会为单行工作:

            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
            

            【讨论】:

              【解决方案8】:

              从 SQL Server 2016+ 开始,引入了查询存储功能来监控性能。它提供了对查询计划选择和性能的洞察。 它不是跟踪或扩展事件的完全替代,但随着它从一个版本到另一个版本的演变,我们可能会在 SQL Server 的未来版本中获得一个功能齐全的查询存储。 Query Store的主要流程

              1. SQL Server 现有组件通过使用查询存储管理器与查询存储进行交互。
              2. 查询存储管理器确定应使用哪个存储,然后将执行传递给该存储(计划或运行时统计或查询等待统计)
                • 计划存储 - 保存执行计划信息
                • Runtime Stats Store - 保存执行统计信息
                • Query Wait Stats Store - 保留等待统计信息。
              3. 计划、运行时统计和等待存储使用查询存储作为 SQL Server 的扩展。

              1. 启用查询存储:查询存储在服务器上的数据库级别工作。

                • 默认情况下,查询存储对新数据库不活动。
                • 您不能为主数据库或tempdb 数据库启用查询存储。
                • 可用的 DMV

                  sys.database_query_store_options (Transact-SQL)

              2. 在查询存储中收集信息:我们使用查询存储 DMV(数据管理视图)从三个存储中收集所有可用信息。

              注意:查询等待统计信息存储仅在 SQL Server 2017+ 中可用

              【讨论】:

                【解决方案9】:

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

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

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

                1. 单击 SSMS 中的 New Query 按钮并将查询文本粘贴到查询文本窗口中。右键单击并从上下文菜单中选择“显示估计的执行计划”选项。

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

                1. 估计的执行计划将在 ApexSQL 计划中打开,可以对其进行分析以进行查询优化。

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

                要查看查询的实际执行计划,请从前面提到的第二步继续,但现在,一旦显示了估计计划,请单击 ApexSQL 计划主功能区栏中的“实际”按钮。

                单击“实际”按钮后,将显示实际执行计划以及成本参数的详细预览以及其他执行计划数据。

                更多关于查看执行计划的信息可以关注this link

                【讨论】:

                  【解决方案10】:

                  获取执行计划的方法有很多种,具体使用哪一种取决于您的具体情况。通常您可以使用 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 - 显示基于基本文本的估计执行计划,而不执行查询
                  • 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 分析机制旨在最大限度地减少对数据库的影响,但这并不意味着不会对性能产生任何影响。如果您的数据库被大量使用,您可能还会在过滤和识别跟踪中的正确计划时遇到问题。您显然应该咨询您的 DBA,看看他们是否对您在他们宝贵的数据库上执行此操作感到满意!

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

                  你得到的计划相当于SQL Server Management Studio中的“包含实际执行计划”选项。

                  方法 4 - 检查查询缓存

                  如果您不能直接运行查询并且也无法捕获分析器跟踪,那么您仍然可以通过检查 SQL 查询计划缓存来获得估计计划。

                  我们通过查询 SQL Server DMVs 检查计划缓存。以下是一个基本查询,它将列出所有缓存的查询计划(作为 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 以在新窗口中打开计划 - 右键单击​​并选择“将执行计划另存为...”以将计划保存为 XML 格式的文件。

                  注意事项:

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

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

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

                  实际执行计划是 SQL Server 实际运行查询的执行计划,而估计执行计划 SQL Server 会计算出它不执行查询。尽管在逻辑上是等效的,但实际的执行计划更有用,因为它包含有关执行查询时实际发生的情况的额外详细信息和统计信息。这在诊断 SQL Server 估计不正确的问题时(例如统计数据过期时)至关重要。

                  如何解释查询执行计划?

                  这个话题本身就值得一个(免费)book 使用。

                  另见:

                  【讨论】:

                  • 给未来读者的注意事项:将SET STATISTICS XML ON ant 放在查询的开头,并将SET STATISTICS XML OFF|ON 周围的区域不想显示在计划输出中:当查询包含您不想/不需要在执行计划中看到的迭代(WHILE)时,我发现这很有用(否则 SQL SERVER 显示它会太重太长)。
                  • @MonsterMMORPG 你可以使用方法4然后选择它。例如,使用 github.com/StackExchange/dapper-dot-net">Dapper.net</…> connection.Query("SELECT 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) WHERE TEXT LIKE N'%Your Original Query Goes Here%'"); %'s 如果您只使用查询的子集。
                  • @Justin 您链接到的用于解释查询执行计划的书的第 2 版日期为 2009 年。您仍然会说它是 2016 年用于此目的的非常好的资源吗?
                  • @Abdul 同一作者 Grant Fritchey 有一本名为 SQL Server Query Performance Tuning 的新书,其中涵盖了 SQL Server 的较新版本。
                  • @Justin,我尝试使用方法 4 - 检查查询缓存,但它返回系统和用户定义的两个查询。有什么办法可以不考虑系统查询?
                  【解决方案11】:

                  假设您使用的是 Microsoft SQL Server Management Studio

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

                  • 实际查询计划,可以按Ctrl+ M 或执行查询前的以下按钮。

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

                  【讨论】:

                    【解决方案12】:

                    可以通过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
                    

                    创建会话后,(在 SSMS 中)转到对象资源管理器并深入研究管理 |扩展活动 |会议。右键单击“GetExecutionPlan”会话并启动它。再次右键单击它并选择“观看实时数据”。

                    接下来,打开一个新的查询窗口并运行一个或多个查询。这是 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 代码和屏幕截图是从带有 SP2 的 SQL/SSMS 2012 生成的。如果您使用的是 SQL 2008/R2,您可能能够调整脚本以使其运行。但该版本没有 GUI,因此您必须提取 showplan XML,将其保存为 *.sqlplan 文件并在 SSMS 中打开。这很麻烦。 SQL 2005 或更早版本中不存在 XEvent。因此,如果您不在 SQL 2012 或更高版本上,我强烈建议您在此处发布其他答案之一。

                    【讨论】:

                      【解决方案13】:

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

                      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
                      

                      【讨论】:

                        猜你喜欢
                        • 2020-04-30
                        • 2011-07-01
                        • 1970-01-01
                        • 2021-09-30
                        • 1970-01-01
                        • 2020-10-13
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多