Почему какой-то запрос sql намного медленнее при использовании с SqlCommand?
У меня есть хранимая процедура, которая выполняется намного быстрее из Sql Server Management Studio (2 секунды), чем при запуске с System.Data.SqlClient.SqlCommand
(время истекает через 2 минуты).
Что может быть причиной этого?
Подробнее:
В Sql Server Management Studio выполняется через 2 секунды (в производственной базе данных):
EXEC sp_Stat
@DepartmentID = NULL
В .NET/С# следующие тайм-ауты через 2 минуты (в производственной базе данных):
string selectCommand = @"
EXEC sp_Stat
@DepartmentID = NULL";
string connectionString = "server=***;database=***;user id=***;pwd=***";
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(selectCommand, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
}
}
}
}
Я также пробовал с selectCommand = "sp_Stat"
, CommandType = StoredProcedure
и a SqlParameter
, но это тот же результат.
И без EXEC
это тот же результат.
В почти базе данных, свободной от данных, оба случая заканчиваются менее чем за 1 секунду. Таким образом, это связано с тем, что в базе данных много данных, но, похоже, это происходит только из .NET...
То, что написал Марк Гравелл о разных значениях SET
, делает разницу в представленном случае.
Профилировщик SQL Server показал, что Sql Server Management Studio запускает следующее SET
, которое поставщик данных .NET Sql не выполняет:
SET ROWCOUNT 0
SET TEXTSIZE 2147483647
SET NOCOUNT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ARITHABORT ON
SET LOCK_TIMEOUT -1
SET QUERY_GOVERNOR_COST_LIMIT 0
SET DEADLOCK_PRIORITY NORMAL
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET ANSI_NULLS ON
SET ANSI_NULL_DFLT_ON ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET CURSOR_CLOSE_ON_COMMIT OFF
SET IMPLICIT_TRANSACTIONS OFF
SET QUOTED_IDENTIFIER ON
SET NOEXEC, PARSEONLY, FMTONLY OFF
Когда я включил их, один и тот же запрос занял одинаковое количество времени в SSMS и .NET.
И ответственный SET
- это...
SET ARITHABORT ON
Что я узнал? Возможно, использовать профайлер вместо угадывания...
(Решение сначала казалось связанным с параметризацией, но я кое-что перепутал...)
Ответы
Ответ 1
Еще одна важная вещь - параметры SET
, которые включены. Некоторые из этих параметров значительно изменяют план запроса, чтобы изменить профиль. Некоторые могут иметь огромное влияние, если вы посмотрите (например) на вычисляемый + упорный (и, возможно, проиндексированный) столбец: если параметры SET
несовместимы, его можно заставить пересчитать значения, а не используя индексированное значение - которое может изменить поиск индекса в сканирование таблицы + вычисление.
Попробуйте использовать профилировщик, чтобы узнать, какие опции SET
"находятся в игре", и посмотрите, меняются ли эти параметры.
Другим воздействием является строка соединения; например, если вы включите MARS, который может изменить поведение тонким образом.
Наконец, транзакции (неявные (TransactionScope
) или явные) могут иметь огромное влияние в зависимости от уровня изоляции.
Ответ 2
Это почти наверняка связано с "неправильным" кэшированным планом запросов. Это произошло на SO довольно много раз.
У вас есть актуальная статистика? Регулярный план обслуживания плановых индексов?
Вы можете проверить, определенно ли это из-за плана кешированных запросов, добавив это в определение хранимой процедуры:
CREATE PROCEDURE usp_MyProcedure WITH RECOMPILE...
Это приведет к повторной индексации всей базы данных (осторожно, если база данных очень велика!):
exec sp_msforeachtable "dbcc dbreindex('?')"
Сообщения SO:
Большая разница во времени выполнения хранимого процесса между Managment Studio и TableAdapter.
Параметр Sniffing (или Spoofing) в SQL Server
оптимизировать для неизвестного для SQL Server 2005?
Различные планы выполнения для той же хранимой процедуры
Ответ 3
У нас была аналогичная проблема, когда запрос завершился через 2 секунды в SSMS и потребовался более 90 секунд при вызове из .NET-клиента (мы написали несколько приложений/сайтов VB/С# для его проверки).
Мы подозревали, что план запроса будет другим, и переписал запрос с явным циклом (подсказки "внутреннее соединение цикла" и "с индексом" ). Это решило проблему.
Ответ 4
Имел аналогичную проблему, и оказалось, что MultipleActiveResultSets = true в строке соединения (которая должна иметь минимальное влияние) заключалась в том, что вытягивание записей на 1.5 мили по удаленному соединению занимает 25 минут, а не около 2 минут.