【问题标题】:Programatically read SQL Server's query plan suggested indexes for a specific execution of SQL?以编程方式读取特定 SQL 执行的 SQL Server 查询计划建议索引?
【发布时间】:2015-05-13 17:00:37
【问题描述】:

如果我在 SSMS 中运行此命令:

set showplan_xml on
GO
exec some_procedure 'arg1', 'arg2','arg3'
GO
set showplan_xml off
GO

我得到了查询执行中涉及的完整调用堆栈的 XML 输出,以及任何建议的索引等。

如何从 C# 中读取此内容?

(一个用例可能是定期启用此功能并将这些结果记录在生产环境中以密切关注索引建议。)

【问题讨论】:

  • 为什么不直接通过SqlCommand 发送这些命令?然后使用 ExecuteScalar() 应该返回 XML 数据。
  • @srutzky 我没有,谢谢你的 ping,明天会读一读!

标签: c# sql-server sqlclr sql-execution-plan


【解决方案1】:

这在大多数情况下是两个独立(尽管相关)的问题。

是否有可能捕获或以某种方式获取缺失索引信息?

  • 如果您只想要 建议索引(并且不关心执行计划的其余部分),那么使用与缺失索引关联的 DMV 可能会更好。您只需要编写一些查询而不是应用程序代码。当然,只要服务重新启动,DMV 信息就会重置,但是如果您想要/需要保留历史记录,您可以将查询结果捕获到表中。请参阅以下 MSDN 页面了解完整详情:

    我可以看到捕获执行计划以获取此信息的唯一好处是它将包含导致建议的查询文本,这显然非常适合进行确定要实施哪些索引的研究,但会如果一个或多个查询的许多变体导致相同的建议索引,也可能会增加数据的行数。只是需要记住的一点。

  • 以编程方式实现建议的索引。它们是供审查和考虑的。那时,它们会根据每个查询进行评估,并且不考虑:
    • 表上已有多少索引
    • 其他查询可能会从类似索引中受益(意思是,可能存在对任何单个查询都不明显但有助于 3 个或更多查询的字段组合,因此只添加一个索引而不是 3 个或更多表)。

是否可以以编程方式捕获执行计划?

是的,这绝对是可行的,我自己已经做到了。无论是控制台应用程序、Windows 窗体、Web 应用程序、SQLCLR 等,您都可以在 .NET 中完成。

如果您想捕获 XML 计划,以下是您需要了解的详细信息:

  • XML 执行计划是:
    • 作为单独的结果集发送
    • 作为NVARCHAR/string的数据类型发送
    • 有两种类型:估计实际
  • 估计计划:
    • 只是:估计
    • 如果你执行返回:SET SHOWPLAN_XML ON;
    • 如果批处理中有超过 1 个查询,则仅返回 1 个将包含多个查询的计划
    • 将返回简单查询的计划,例如 SELECT 1DECLARE @Bob INT; SET @Bob = 52;
    • 执行任何查询。因此,此方法将返回单个结果集作为执行计划
  • 实际计划:
    • 是真正的交易,哟!
    • 如果你执行返回:SET STATISTICS XML ON;
    • 每个查询返回 1 个计划作为单独的结果集
    • 不会返回简单查询的计划,例如 SELECT 1DECLARE @Bob INT; SET @Bob = 52;
    • 执行批处理中的所有查询。因此,
      • 每次查询,该方法会返回一个两个结果集:如果查询返回数据,则查询结果为第一个结果集,执行计划为唯一的结果集(如果查询不返回数据)或第二个结果集
      • 对于多个查询,执行计划将穿插在任何查询结果中。但是,由于某些查询不返回任何结果,因此您不能简单地捕获所有其他结果集。我测试了结果集中的单个字段,类型为 NVARCHAR,字段名称为 Microsoft SQL Server 2005 XML Showplan(至少在 SQL Server 2014 之前是一致的;我尚未测试 SQL Server 2016)。
      • 出于测试目的,您可能希望将这些查询包装在 BEGIN TRAN; / COMMIT TRAN; 中,这样就不会发生实际的数据修改。
  • SET 命令需要在自己的批处理中,所以通过以下方式获取计划:

    SqlConnection _Connection = new sqlConnection(_ConnectionStringFromSomewhere);
    SqlCommand _Command = _Connection.CreateCommand();
    SqlDataReader _Reader = null;
    
    try
    {
      _Connection.Open();
    
      // SET command needs to be in its own batch
      _Command.CommandText = "SET something ON";
      _Command.ExecuteNonQuery();
    
      // Now we can run the desired query
      _Command.CommandText = _QueryToTest;
      _Reader = _Command.ExecuteReader();
    
      ..get you some execution plans!
    }
    finally
    {
      if (_Reader != null)
      {
        _Reader.Dispose();
      }
      _Command.Dispose();
      _Connection.Dispose();
    }
    

作为最后一点,我将提到,对于任何有兴趣捕获执行计划但有兴趣编写任何代码来获取它们的人,我已经将其实现为 SQLCLR 存储过程。该过程不仅获取 XML 执行计划,还获取来自 STATISTICS TIMESTATISTICS IO 的输出,这两者都作为消息返回(就像 PRINT 语句一样)更难捕获。并且,所有 3 种类型的输出的结果都可以捕获到表格中,以便在多次执行中进行进一步分析(方便对当前和修改后的代码进行 A/B 比较)。这在SQL# SQLCLR 库中可用(我也是该库的作者)。请注意,虽然 SQL# 有免费版,但这个特殊的存储过程 DB_GetQueryInfo 仅在完整版中可用,而不是免费版。


更新:
有趣的是,我刚刚看到了下面的 MSDN 文章,该文章描述了如何使用 SQLCLR 来获取估计的计划,提取估计的成本,将其作为 SQLCLR 存储过程的 OUTPUT 参数传回,然后根据它做出决定。我不认为我会将它用于这样的目的,但考虑到这篇文章是在 2005 年写的,这非常有趣:

Processing XML Showplans Using SQLCLR in SQL Server 2005

【讨论】:

  • 优秀。总有一天你应该写一本书!
  • @tbone 感谢您的赞美。实际上,我很愿意。 Stairway to SQLCLR 系列的部分想法是,它将成为有关该主题的书籍的基础。不幸的是(至少在一定程度上),生活(即 3 个年幼的孩子等)有一种阻碍方式。但是,如果有人(提示,提示)想自愿承担我的家务和其他责任,那么我将有时间完成它 8-D ....否则,这将是一两年...希望更少超过 10 ;-)..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 2011-07-01
  • 2020-10-13
相关资源
最近更新 更多