查询代码非常慢,但在SSMS中速度很快

我有一个相当简单的查询,我不断地得到超时(它需要三分钟完成,我停止了它,所以我可以发布这个问题),当它在代码中运行,但是当我从同一台计算机运行相同的查询在Sql Server Management Studio中,当数据未在服务器上caching时,查询将仅花费2532 ms的第一个查询,而对于重复查询,则仅为524 ms

这是我的C#代码

 using (var conn = new SqlConnection("Data Source=backend.example.com;Connect Timeout=5;Initial Catalog=Logs;Persist Security Info=True;User ID=backendAPI;Password=Redacted")) using (var ada = new SqlDataAdapter(String.Format(@" SELECT [PK_JOB],[CLIENT_ID],[STATUS],[LOG_NAME],dt FROM [ES_HISTORY] inner join [es_history_dt] on [PK_JOB] = [es_historyid] Where client_id = @clientID and dt > @dt and (job_type > 4 {0}) {1} Order by dt desc" , where.ToString(), (cbShowOnlyFailed.Checked ? "and Status = 1" : "")), conn)) { ada.SelectCommand.Parameters.AddWithValue("@clientID", ClientID); ada.SelectCommand.Parameters.AddWithValue("@dt", dtpFilter.Value); //ada.SelectCommand.CommandTimeout = 60; conn.Open(); Logs.Clear(); ada.Fill(Logs); //Time out exception for 30 sec limit. } 

这里是我在SSMS中运行的代码,我从ada.SelectCommand.CommandText中取出它

 declare @clientID varchar(200) set @clientID = '138' declare @dt datetime set @dt = '9/19/2011 12:00:00 AM' SELECT [PK_JOB],[CLIENT_ID],[STATUS],[LOG_NAME],dt FROM [ES_HISTORY] inner join [es_history_dt] on [PK_JOB] = [es_historyid] Where client_id = @clientID and dt > @dt and (job_type > 4 or job_type = 0 or job_type = 1 or job_type = 4 ) Order by dt desc 

什么原因导致了时间差异的主要差异?


为了保持评论部分的清洁,我会在这里回答一些常见问题。

同一台计算机和login用于应用程序和ssms。

我的示例查询中只返回了15行。 但是, es_history包含11351699 rowses_history_dt包含8588493 rows 。 这两个表都很好的索引,SSMS的执行计划表示,他们正在使用索引查找查找,所以他们快速查找。 该程序的行为就好像它没有使用查询的C#版本的索引。

您在SSMS中的代码与您在应用程序中运行的代码不同。 应用程序中的这一行添加了一个NVARCHAR参数:

  ada.SelectCommand.Parameters.AddWithValue("@clientID", ClientID); 

而在SSMS脚本中,您将其声明为VARCHAR:

 declare @clientID varchar(200) 

由于数据types优先级的规则,其中@clientID的types为NVARCHAR(我做了一个信念的飞跃,并假设client_id列的types为VARCHAR),查询中的Where client_id = @clientIDexpression式不是SARG- @clientID 。 因此,应用程序强制执行表扫描,SSMS查询可以快速寻找键。 这是使用Parameters.AddWithValue的一个众所周知和理解的问题,并已在之前的许多文章中讨论过,例如。 请参阅数据访问代码如何影响数据库性能 。 一旦了解问题,解决scheme就变得微不足道:

  • 使用接受types的构造函数添加参数: Parameters.Add("@clientID", SqlDbType.Varchar, 200) (并传递显式长度以防止caching污染,请参阅未指定参数长度时的查询性能和计划caching问题正确地

  • 在SQL文本where client_id = cast(@clientID as varchar(200))参数: where client_id = cast(@clientID as varchar(200))

第一个解决scheme是优越的,因为它解决了高速caching污染问题以及SARG能力问题。

我还build议您阅读应用程序中的慢速,SSMS中的快速? 了解性能之谜

在你的c#连接上运行profiler – 可能还有其他的活动,你不知道。

在手动运行查询时从SSMS捕获执行计划,然后在运行应用程序时从Profiler捕获执行计划。 比较和对比。

像这里所build议的那样运行DBCC FREEPROCCACHE ,以确保问题不是由于陈旧的查询执行计划造成的。