以编程方式读取 SQL 服务器查询计划为 SQL 的特定执行建议的索引?
Programatically read SQL Server's query plan suggested indexes for a specific execution of SQL?
如果我在 SSMS 中 运行 这个命令:
set showplan_xml on
GO
exec some_procedure 'arg1', 'arg2','arg3'
GO
set showplan_xml off
GO
我得到 XML 查询执行中涉及的完整调用堆栈的输出,以及任何建议的索引等。
如何从 C# 中阅读此内容?
(一个用例可能是定期启用此功能并将这些结果记录在生产环境中以关注索引建议。)
在大多数情况下,这是两个独立(但相关)的问题。
是否可以捕获或以某种方式获取缺失索引信息?
如果您只想要建议的索引(并且不关心执行计划的其余部分),那么您最好使用与缺失索引关联的 DMV。您只需要编写一些查询而不是应用程序代码。当然,每当服务重新启动时,DMV 信息都会重置,但如果您 want/need 保留历史记录,则可以将查询结果捕获到 table 中。有关完整详细信息,请参阅以下 MSDN 页面:
- sys.dm_db_missing_index_groups
- sys.dm_db_missing_index_group_stats
- sys.dm_db_missing_index_details
- sys.dm_db_missing_index_columns
我可以看到捕获执行计划以获取此信息的唯一好处是它将包含产生建议的查询文本,这显然对于确定要实施哪些索引的研究非常有用,但如果一个或多个查询的许多变体产生相同的建议索引,也可能会激增数据行数。只是要记住一些事情。
- 不要以编程方式实施建议的索引。它们供审查和考虑。它们是根据当时的每个查询进行评估的,并且不考虑:
- table
上已有多少索引
- 哪些其他查询可能会从类似的索引中受益(意思是,可能存在对任何单个查询都不明显的字段组合,但有助于 3 个或更多查询,因此只添加一个索引而不是 3 个或更多 table).
是否可以通过编程方式捕获执行计划?
是的,这绝对是可行的,我自己也做到了。无论是控制台应用程序、Windows 表单、Web 应用程序、SQLCLR 等,您都可以在 .NET 中完成。
如果您想获取 XML 计划,以下是您需要了解的详细信息:
- XML 执行计划是:
- 作为单独的结果集发送
- 作为
NVARCHAR
/string
的数据类型发送
- 两种类型:估计和实际
- 估计 计划:
- 只是:估计
如果您执行 - returned:
SET SHOWPLAN_XML ON;
- return 如果批处理中有超过 1 个查询,则只有 1 个计划将包含多个查询
- 将 return 计划简单查询,例如
SELECT 1
和 DECLARE @Bob INT; SET @Bob = 52;
- 执行不执行任何查询。因此,此方法将 return 单个结果集作为执行计划
- 实际 计划:
- 正品哟!
如果您执行 - returned:
SET STATISTICS XML ON;
- return 1 个计划 每个查询 作为单独的结果集
- 不会不会 return计划简单查询,例如
SELECT 1
和DECLARE @Bob INT; SET @Bob = 52;
- 批量执行所有查询。因此,
- Per query,这个方法会return一个或两个结果集:如果查询return s数据,则查询结果将是第一个结果集,执行计划将是唯一的结果集(如果查询没有return数据)或第二个结果集
- 对于多个查询,执行计划将散布在任何查询结果中。但是,由于某些查询没有 return 任何结果,您不能简单地捕获所有其他结果集。我测试结果集中的单个字段,类型为
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 TIME
和 STATISTICS IO
的输出,这两者都更难捕获,因为它们是 returned作为消息(就像 PRINT
语句)。并且,所有 3 种类型的输出结果都可以捕获到 tables 中,以便跨多个执行进行进一步分析(方便对当前代码和修改后的代码进行 A/B 比较)。这在 SQL# SQLCLR 库中可用(同样,我是它的作者)。请注意,虽然 SQL# 有免费版,但这个特定的存储过程 DB_GetQueryInfo 仅在完整版中可用,在免费版中不可用.
更新:
有趣的是,我刚刚 运行 浏览了以下 MSDN 文章,该文章描述了如何使用 SQLCLR 获取估计计划,提取估计成本,将其作为 [=170= 的 OUTPUT 参数传回]CLR Stored Procedure,然后据此做出决定。我认为我不会将它用于这样的目的,但鉴于这篇文章是在 2005 年写的,所以非常有趣:
如果我在 SSMS 中 运行 这个命令:
set showplan_xml on
GO
exec some_procedure 'arg1', 'arg2','arg3'
GO
set showplan_xml off
GO
我得到 XML 查询执行中涉及的完整调用堆栈的输出,以及任何建议的索引等。
如何从 C# 中阅读此内容?
(一个用例可能是定期启用此功能并将这些结果记录在生产环境中以关注索引建议。)
在大多数情况下,这是两个独立(但相关)的问题。
是否可以捕获或以某种方式获取缺失索引信息?
如果您只想要建议的索引(并且不关心执行计划的其余部分),那么您最好使用与缺失索引关联的 DMV。您只需要编写一些查询而不是应用程序代码。当然,每当服务重新启动时,DMV 信息都会重置,但如果您 want/need 保留历史记录,则可以将查询结果捕获到 table 中。有关完整详细信息,请参阅以下 MSDN 页面:
- sys.dm_db_missing_index_groups
- sys.dm_db_missing_index_group_stats
- sys.dm_db_missing_index_details
- sys.dm_db_missing_index_columns
我可以看到捕获执行计划以获取此信息的唯一好处是它将包含产生建议的查询文本,这显然对于确定要实施哪些索引的研究非常有用,但如果一个或多个查询的许多变体产生相同的建议索引,也可能会激增数据行数。只是要记住一些事情。
- 不要以编程方式实施建议的索引。它们供审查和考虑。它们是根据当时的每个查询进行评估的,并且不考虑:
- table 上已有多少索引
- 哪些其他查询可能会从类似的索引中受益(意思是,可能存在对任何单个查询都不明显的字段组合,但有助于 3 个或更多查询,因此只添加一个索引而不是 3 个或更多 table).
是否可以通过编程方式捕获执行计划?
是的,这绝对是可行的,我自己也做到了。无论是控制台应用程序、Windows 表单、Web 应用程序、SQLCLR 等,您都可以在 .NET 中完成。
如果您想获取 XML 计划,以下是您需要了解的详细信息:
- XML 执行计划是:
- 作为单独的结果集发送
- 作为
NVARCHAR
/string
的数据类型发送
- 两种类型:估计和实际
- 估计 计划:
- 只是:估计 如果您执行
- returned:
SET SHOWPLAN_XML ON;
- return 如果批处理中有超过 1 个查询,则只有 1 个计划将包含多个查询
- 将 return 计划简单查询,例如
SELECT 1
和DECLARE @Bob INT; SET @Bob = 52;
- 执行不执行任何查询。因此,此方法将 return 单个结果集作为执行计划
- 实际 计划:
- 正品哟! 如果您执行
- returned:
SET STATISTICS XML ON;
- return 1 个计划 每个查询 作为单独的结果集
- 不会不会 return计划简单查询,例如
SELECT 1
和DECLARE @Bob INT; SET @Bob = 52;
- 批量执行所有查询。因此,
- Per query,这个方法会return一个或两个结果集:如果查询return s数据,则查询结果将是第一个结果集,执行计划将是唯一的结果集(如果查询没有return数据)或第二个结果集
- 对于多个查询,执行计划将散布在任何查询结果中。但是,由于某些查询没有 return 任何结果,您不能简单地捕获所有其他结果集。我测试结果集中的单个字段,类型为
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 TIME
和 STATISTICS IO
的输出,这两者都更难捕获,因为它们是 returned作为消息(就像 PRINT
语句)。并且,所有 3 种类型的输出结果都可以捕获到 tables 中,以便跨多个执行进行进一步分析(方便对当前代码和修改后的代码进行 A/B 比较)。这在 SQL# SQLCLR 库中可用(同样,我是它的作者)。请注意,虽然 SQL# 有免费版,但这个特定的存储过程 DB_GetQueryInfo 仅在完整版中可用,在免费版中不可用.
更新:
有趣的是,我刚刚 运行 浏览了以下 MSDN 文章,该文章描述了如何使用 SQLCLR 获取估计计划,提取估计成本,将其作为 [=170= 的 OUTPUT 参数传回]CLR Stored Procedure,然后据此做出决定。我认为我不会将它用于这样的目的,但鉴于这篇文章是在 2005 年写的,所以非常有趣: