为什么通过ADO.NET执行的存储过程比SQL Server Management Studio花费许多倍的时间?

Gid*_*sey 2 c# t-sql sql-server ado.net

我有一个正在从超时的旧版ASP.NET WebForms应用程序执行的存储过程。该CommandTimeout属性被设置为6分钟。该存储过程查询一个表中有1500万行,并联接到其他表...。因此它很慢。但是,它仅返回20行,因此此处的网络性能不是问题。

var data = new DataSet();

using (var connection = GetConnection())
using(var command = connection.CreateCommand())
using(var dataAdapter = new SqlDataAdapter(command))
{
   command.CommandTimeout = 360;
   command.CommandType = CommandType.StoredProcedure;
   command.CommandText = "praGetTradesForProductIdReassignment";
   command.Parameters.AddWithValue("@int4_TradeId", (object)tradeId ?? DBNull.Value);
   command.Parameters.AddWithValue("@int4_FeedId", (object)feedId ?? DBNull.Value);
   command.Parameters.AddWithValue("@int4_SystemId", (object)systemId ?? DBNull.Value);
   command.Parameters.AddWithValue("@int4_TradeCode", (object)tradeCode ?? DBNull.Value);
   command.Parameters.AddWithValue("@int4_ProductID", (object)productId ?? DBNull.Value);
   command.Parameters.AddWithValue("@sortColumn", sortColumn);
   command.Parameters.AddWithValue("@sortDirection", sortDirection);
   command.Parameters.AddWithValue("@pageIndex", pageIndex);
   command.Parameters.AddWithValue("@pageSize", pageSize);

   dataAdapter.Fill(data);                
}

return data;
Run Code Online (Sandbox Code Playgroud)

当我在SQL Server Management Studio中使用相同的参数执行相同的存储过程时,它将在大约50秒后成功完成。

我已经使用Profiler运行了SQL跟踪,它显示ADO.NET执行的时间比SQL Server Management Studio的执行时间长(50 vs 356秒)。

  <Events>
    <Event id="13" name="SQL:BatchStarting">
      <Column id="1" name="TextData">exec praGetTradesForProductIdReassignment @int4_TradeId=NULL,@int4_FeedId=NULL,@int4_SystemId=NULL,@int4_TradeCode=NULL,@int4_ProductID=NULL,@sortColumn=N'',@sortDirection=0,@pageIndex=0,@pageSize=20</Column>
      <Column id="10" name="ApplicationName">Microsoft SQL Server Management Studio - Query</Column>
    </Event>
    <Event id="12" name="SQL:BatchCompleted">
      <Column id="10" name="ApplicationName">Microsoft SQL Server Management Studio - Query</Column>
      <Column id="1" name="TextData">exec praGetTradesForProductIdReassignment @int4_TradeId=NULL,@int4_FeedId=NULL,@int4_SystemId=NULL,@int4_TradeCode=NULL,@int4_ProductID=NULL,@sortColumn=N'',@sortDirection=0,@pageIndex=0,@pageSize=20</Column>
      <Column id="13" name="Duration">49946289</Column>
    </Event>
    <Event id="10" name="RPC:Completed">
      <Column id="10" name="ApplicationName">.Net SqlClient Data Provider</Column>
      <Column id="1" name="TextData">exec praGetTradesForProductIdReassignment @int4_TradeId=NULL,@int4_FeedId=NULL,@int4_SystemId=NULL,@int4_TradeCode=NULL,@int4_ProductID=NULL,@sortColumn=N'',@sortDirection=0,@pageIndex=0,@pageSize=20</Column>
      <Column id="13" name="Duration">356352721</Column>
    </Event>
  </Events>
Run Code Online (Sandbox Code Playgroud)

为什么ADO.NET执行比SSMS执行花费更长的时间?

这个问题的目的不是提高存储过程的性能,而是确定通过ADO.NET与SSMS进行调用之间的性能差异的原因。

小智 6

首先,重要的是要知道ADO.NET和SSMS(SQL Server Management Studio)是不同的实体。因此,您应该考虑查询执行中的某些行为/操作更改。

第1部分:为什么SQL查询在SSMS中的运行速度比ADO.NET快?

当您在SSMS中运行任何查询时,它总是为其创建一个查询计划。查询计划通过各种优化以及搜索结果的索引执行。您可以想象此过程涉及某种缓存。

另外,当Microsoft开发SSMS和ADO.NET时,并非所有参数都相同。您会发现ANSI_NULLS之类的参数设置,ARITHABORT与针对ADO.NET的SSMS中的参数不同

如果要确保SQL查询的执行符合SSMS本身,请确保两种情况下的设置都相同。

第2部分:您可以尝试什么?

您可以尝试几种方法来解决问题。

启用ARITHABORT参数:ATITHABORT的设置对于ADO.NET和SSMS是不同的。您可以尝试在查询中打开ARITHABORT并执行。

SET ARITHABORT ON
Run Code Online (Sandbox Code Playgroud)

启用ANSI_NULLS:在查询文本中将ANSI_NULLS的设置更改为Enabled。

SET ANSI_NULLS ON
Run Code Online (Sandbox Code Playgroud)

以类似的方式,您可以在SSMS和ADO.NET中进行相同的设置,从而获得非常接近的执行结果。

第3部分:参数嗅探

最后但并非最不重要,了解查询的结构也很重要。您可以通过在存储过程中使用局部变量来优化SQL查询,并尽可能使用游标等等。

第4部分:参考

  1. Erland Sommarskog的文章

  2. 参数嗅探

希望对您有帮助...一切顺利!