Запрос чрезвычайно медленный в коде, но быстрый в SSMS

У меня есть довольно простой запрос, который я продолжаю получать тайм-ауты (выполнение занимает более трех минут, я остановил его раньше, чтобы опубликовать этот вопрос), когда он работает в коде, однако, когда я запускаю тот же запрос с того же компьютера в Sql Server Management Studio запрос будет принимать только 2532 ms первый запрос, когда данные не кэшируются на сервере, и 524 ms для повторных запросов.

Вот мой код С#

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 rows, а es_history_dt содержит 8588493 rows. Обе таблицы хорошо проиндексированы, и план выполнения в SSMS говорит, что они используют поиск по индексу для поиска, поэтому поиск выполняется быстро. Программа ведет себя так, как будто она не использует индексы для версии запроса C#.


person Scott Chamberlain    schedule 03.10.2011    source источник
comment
Вы использовали того же пользователя в SSMS, что и в коде?   -  person bzlm    schedule 03.10.2011
comment
Сколько строк возвращает этот запрос?   -  person tom redfern    schedule 03.10.2011


Ответы (4)


Ваш код в SSMS — это не тот код, который вы запускаете в своем приложении. Эта строка в вашем приложении добавляет параметр NVARCHAR:

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

в то время как в сценарии SSMS вы объявляете его как VARCHAR:

declare @clientID varchar(200)

В соответствии с правилами приоритета типов данных выражение Where client_id = @clientID в вашем запросе не поддерживает SARG, где @clientID имеет тип NVARCHAR (я делаю прыжок веры и предполагаю, что столбец client_id имеет тип VARCHAR). Таким образом, приложение принудительно сканирует таблицу, где запрос SSMS может выполнять быстрый поиск ключа. Это хорошо известная и понятная проблема с использованием Parameters.AddWithValue, которая обсуждалась во многих статьях ранее, например. см. Как код доступа к данным влияет на производительность базы данных. Как только проблема понятна, решения тривиальны:

Первое решение лучше, потому что оно решает проблему загрязнения кеша в дополнение к проблеме способности SARG.

Я также рекомендую вам прочитать Медленно в приложении, быстро в SSMS? Понимание секретов производительности

person Remus Rusanu    schedule 03.10.2011
comment
На самом деле я использовал varchar, потому что интерфейс функции передал идентификатор клиента в виде строки. Я проверил схему, и Client_ID на самом деле является целым числом. Изменение его в моем коде для преобразования переданной строки в int перед выполнением запроса решило проблему. Спасибо! - person Scott Chamberlain; 03.10.2011

Запустите профилировщик на вашем соединении С# - могут быть другие действия, о которых вы не знаете.

person Jimbo    schedule 03.10.2011

Захватите план выполнения из SSMS, когда вы вручную запускаете свой запрос, а затем из Profiler, когда вы запускаете свое приложение. Сравнивать и противопоставлять.

person Community    schedule 03.10.2011

Запустите DBCC FREEPROCCACHE, как предлагается здесь , просто чтобы убедиться, что проблема не связана с устаревшим планом выполнения запроса.

person OutstandingBill    schedule 21.05.2016