Обращение к БД без LINQ
История пошла в обратную сторону, и теперь проще найти, как выполнять запросы к БД с помощью LINQ, чем как это сделать старым добрым SQL. Помимо этого, в некоторых случая может неслабо сказаться разница в производительности, что я и предлагаю вам рассмотреть далее на конкретных примерах.
ОС | Windows 7 SP1 x64 |
Язык | C# / .NET 4 |
IDE | Visual Studio |
БД | Microsoft SQL Server |
Пара слов
LINQ очень удобен, с ним приятно работать, будь то LINQ to SQL
или Entity Framework
. Работает автодополнение с IntelliSense, всё видно прямо во время написания, не нужно смотреть в схему БД. Короче, все ништяки ORM.
Но есть вопрос производительности - это и не скрывается - что работа с БД через LINQ проигрывает в скорости обычным запросам SQL. У меня появилась такая задача: сделать выборку данных (id записи и одно поле), затем выполнить поиск по этому полю в другой базе и результат записать в ещё одно место. Записей в исходной таблице больше 50 000. Так вот, через LINQ это всё выполнялось довольно долго, примерно 2000 записей за 10 минут. Я решил проверить, как это будет на чистом SQL, и результат оказался что-то около 3000 записей в минуту. То есть, если свести в таблицу с равными значениями, то:
Язык | Время | Записей обработано |
---|---|---|
LINQ | 10 минут | 2 000 |
SQL | 30 000 |
Вот тут-то я и выпилил все эту линкушные свистелки из проекта.
Практика
Выполнять “голые” SQL запросы без LINQ достаточно просто.
Выполнение простого запроса
Обычный SELECT
к таблице:
// сюда будем складывать полученные данные
Dictionary<long, string> infa = new Dictionary<long, string>();
// объявили соединение
using (SqlConnection sqlConn = new SqlConnection("Data Source=someserver;Initial Catalog=somecatalog;Integrated Security=True"))
// объявили команду (SQL-запрос)
using (SqlCommand cmd = new SqlCommand("SELECT id, name FROM sometable;", sqlConn))
{
// открыли соединение
sqlConn.Open();
// выполнили команду, получили результат
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read()) // перебираем результат
{
// и сохраняем его в наш словарь
infa.Add((long)reader[0], (string)reader[1]);
}
}
Выхов хранимой процедуры с параметрами
Нужно вызвать хранимую процедуру с двумя входными параметрами.
// объявили соединение
using (SqlConnection sqlConn = new SqlConnection("Data Source=someserver;Initial Catalog=somecatalog;Integrated Security=True"))
// объявили команду (SQL-запрос)
using (SqlCommand cmd = new SqlCommand("searchInfoByName", sqlConn))
{
// вот это важный момент, если его не указать, то процедура не будет выполняться, выдавая ошибку про отсутствующие параметры
cmd.CommandType = CommandType.StoredProcedure;
// у нас 50 000 записей, а стандартный таймаут 30 секунд, нам некогда по столько ждать
cmd.CommandTimeout = 5;
// открыли соединение
sqlConn.Open();
// для каждой записи словаря, собранного ранее, выполнить хранимую процедуру
foreach (var rec in infa)
{
try // выполнять будем с перехватом исключений
{
// добавление параметров к процедуре
cmd.Parameters.Add(new SqlParameter("@name", rec.Value));
cmd.Parameters.Add(new SqlParameter("@id", rec.Key));
// данные получать нам не нужно, только вызвать процедуру
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// тут какая-нибудь запись в журнал ошибок
}
// очищаем параметры для следующей итерации
cmd.Parameters.Clear();
}
}
Ещё пара слов
Конечно, смотреть всегда надо исходя из ситуации. Один только этот случай не означает, что никогда и ни за что не надо использовать LINQ. Просто имейте в виду, что когда нужна производительность, то лучше не обвешивать работу с БД дополнительными фреймворками.
Social networks
Zuck: Just ask
Zuck: I have over 4,000 emails, pictures, addresses, SNS
smb: What? How'd you manage that one?
Zuck: People just submitted it.
Zuck: I don't know why.
Zuck: They "trust me"
Zuck: Dumb fucks