我有一个相当简单的查询,当它在代码中运行时,我不断超时(需要三分钟多才能完成,我提前停止了它,所以我可以发布这个问题),但是当我从同一台计算机运行相同的查询时在 Sql Server Management Studio 中,当数据未缓存在服务器上时,第一次查询仅需要 2532 毫秒
,而重复查询则需要 524 毫秒
。
这是我的 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
是什么导致了时间差异的主要差异?
为了保持评论部分的干净,我将在这里回答一些常见问题。
应用程序和 ssms 使用相同的计算机和登录名。
我的示例查询中仅返回 15 行。但是,es_history
包含 11351699 行
,而 es_history_dt
包含 8588493 行
。这两个表都有很好的索引,SSMS 中的执行计划表示它们正在使用索引查找进行查找,因此它们的查找速度很快。该程序的行为就好像它没有使用 C# 版本查询的索引。
I have a fairly simple query that I keep getting timeouts (it takes over three minutes to complete, I stopped it early so I could post this question) on when it is running in code, however when I run the same query from the same computer in Sql Server Management Studio the query will only take 2532 ms
the first query when the data is not cached on the server and 524 ms
for repeated queries.
Here is my c# code
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.
}
here is my code I am running in SSMS, I pulled it right from 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
What is causing the major discrepancy for the difference in time?
To keep the comment section clean, I will answer some FAQ's here.
The same computer and logon is used for both the application and ssms.
Only 15 rows are returned in my example query. However, es_history
contains 11351699 rows
and es_history_dt
contains 8588493 rows
. Both tables are well indexed and the execution plan in SSMS says they are using index seeks for the look-ups so they are fast lookups. The program is behaving as if it is not using the indexes for the C# version of the query.
发布评论
评论(5)
您在 SSMS 中的代码与您在应用程序中运行的代码不同。应用程序中的这一行添加了一个 NVARCHAR 参数:
而在 SSMS 脚本中,您将其声明为 VARCHAR:
由于 数据类型优先级 查询中的
Where client_id = @clientID
表达式不支持 SARG,其中@clientID
为类型 NVARCHAR(我的信念飞跃并假设client_id
列的类型为 VARCHAR)。因此,应用程序强制进行表扫描,其中 SSMS 查询可以执行快速键查找。这是使用Parameters.AddWithValue 时众所周知且易于理解的问题,并且之前已在许多文章中讨论过,例如。请参阅数据访问代码如何影响数据库性能。一旦理解了问题,解决方案就很简单:使用构造函数添加参数接受类型:
Parameters.Add("@clientID", SqlDbType.Varchar, 200)
(并且do传入显式长度以防止缓存污染,请参阅 未正确指定参数长度时的查询性能和计划缓存问题或在 SQL 文本中强制转换参数:
where client_id = cast( @clientID 为varchar(200))
。第一个解决方案是优越的,因为除了 SARG 能力问题之外,它还解决了缓存污染问题。
我还建议您阅读应用程序慢,SSMS 快?了解性能奥秘
Your code in SSMS is not the same code you run in your application. This line in your application adds a NVARCHAR parameter:
while in the SSMS script you declare it as VARCHAR:
Due to the rules of Data Type Precedence the
Where client_id = @clientID
expression in your query is not SARG-able where@clientID
is of type NVARCHAR (I'm making a leap of faith and assume thatclient_id
column is of type VARCHAR). The application thus forces a table scan where the SSMS query can do a quick key seek. This is a well know and understood issue with using Parameters.AddWithValue and has been discussed in many articles before, eg. see How Data Access Code Affects Database Performance. Once the problem is understood, the solutions are trivial:add parameters with the constructor that accepts a type:
Parameters.Add("@clientID", SqlDbType.Varchar, 200)
(and do pass in the explicit length to prevent cache pollution, see Query performance and plan cache issues when parameter length not specified correctlyor cast the parameter in the SQL text:
where client_id = cast(@clientID as varchar(200))
.The first solution is superior because it solves the cache pollution problem in addition to the SARG-ability problem.
I would also recommend you read Slow in the Application, Fast in SSMS? Understanding Performance Mysteries
按照建议运行
DBCC FREEPROCCACHE
这里,只是为了确保问题不是由于过时的查询执行计划造成的。Run
DBCC FREEPROCCACHE
, as suggested here, just to make sure the problem isn't due to a stale query execution plan.遇到同样的问题:
解决方案:
删除存储过程,然后重新创建完全相同的存储过程,现在两者都以毫秒为单位返回。没有代码更改。
Had the same issue:
Solution:
Drop Stored Procedure, then recreate exactly the same stored procedure, now both returning in milliseconds. No code change.
在您的 C# 连接上运行探查器 - 可能还有您不知道的其他活动正在发生。
Run the profiler on your c# connection - there may be other activity going on that you are not aware of.
当您手动运行查询时,从 SSMS 捕获执行计划;当您运行应用程序时,从 Profiler 捕获执行计划。比较和对比。
Capture the execution plan from both SSMS when you manually run your query and then from Profiler when you are running your application. Compare and contrast.