У меня проблема. В приведенном ниже коде есть foreach
l oop, где создается строка запроса для каждого объекта truck
внутри trucks
коллекции. L oop повторяется 100 раз (например, 100 объектов в коллекции). И метод с l oop вызывается 2000 раз.
Я отказался от использования ORM для повышения производительности. Но, к сожалению, я все еще немного разочарован скоростью выполнения кода.
Обратите внимание, что для каждого повторяющегося объекта создается string query
, который, например, выглядит следующим образом:
UPDATE dbo.Trucks
SET OfficialNumber = '5124095'
, Status = 'Undefined'
, PerformancesUpdate = @PerformancesUpdate
, PictureLink = 'http://www.somewebsite.com/photos/middle////.jpg'
WHERE TruckId = 405664;
и затем, после анализа command.Parameters
, если они существуют (в данном случае @PerformancesUpdate
), запрос отправляется библиотечным методом с int rows = command.ExecuteNonQuery(); //execute SQL
. И так, 100 раз. Обратите внимание, что строка запроса зависит от свойств объекта, поэтому каждый случай будет отличаться.
Вот мои вопросы . Мне интересно, возможно ли создать один запрос для всех 100 объектов в l oop и отправить его сразу? Если да, должно ли это повысить производительность? Если да, как должен выглядеть пример запроса?
А вот код, который создает l oop, где создаются и выполняются SQL запросов:
using (SqlConnection connection = GetConnection(_connectionString))
using (SqlCommand command = connection.CreateCommand())
{
connection.Open();
foreach (SomeObject truck in trucks)
{
Console.WriteLine("Updating " + counter++ + " of " + trucks.Count);
//clean up string for basic data
truck.Status = CleanUpStringCases(truck.Status);
truck.Destination = CleanUpStringCases(truck.Destination);
//clean up string for full AIS data
if (dataType == "full")
{
truck.TruckType = CleanUpStringCases(truck.TruckType);
}
//PARSE
SomeObject existing = new SomeObject();
//find existing truck to be updated
if (truck.OfficialNumber > 0) existing = _context.trucks.Where(v => v.OfficialNumber == truck.OfficialNumber).FirstOrDefault();
StringBuilder querySb = new StringBuilder();
if (existing != null)
{
//update for basic data
querySb.Append("UPDATE dbo." + _trucksTableName + " SET OfficialNumber = '" + truck.OfficialNumber + "'");
if (existing.MNCI == 0) if (truck.MNCI.HasValue) querySb.Append(" , MNCI = '" + truck.MNCI + "'");
if (truck.LatestActivity.HasValue) querySb.Append(" , LatestActivity = @LatestActivity");
if (truck.ETA.HasValue) querySb.Append(" , ETA = @ETA");
if (!string.IsNullOrEmpty(truck.Status)) querySb.Append(" , Status = '" + truck.Status + "'");
if (!string.IsNullOrEmpty(truck.Destination)) querySb.Append(" , Destination = '" + truck.Destination + "'");
if (!string.IsNullOrEmpty(truck.Area)) querySb.Append(" , Area = '" + truck.Area + "'");
if (truck.HeadingTo.HasValue) querySb.Append(" , HeadingTo = @HeadingTo");
if (truck.Lat.HasValue) querySb.Append(" , Lat = @Lat");
if (truck.Lon.HasValue) querySb.Append(" , Lon = @Lon");
if (truck.Speed.HasValue)
{
querySb.Append(" , Speed = @Speed");
if ((existing.SpeedMax < truck.Speed || existing.SpeedMax == null) && truck.Speed != 0) querySb.Append(" , SpeedMax = @Speed"); //update speed max
}
//string for full AIS data
if (dataType == "full")
{
if (truck.PerformancesUpdate.HasValue) querySb.Append(" , PerformancesUpdate = @PerformancesUpdate");
if (!string.IsNullOrEmpty(truck.TruckType)) querySb.Append(" , TruckType = '" + truck.TruckType + "'");
if (!string.IsNullOrEmpty(truck.PictureLink)) querySb.Append(" , PictureLink = '" + truck.PictureLink + "'");
if (truck.LOA.HasValue) querySb.Append(" , LOA = '" + truck.LOA + "'");
if (truck.Height.HasValue) querySb.Append(" , Height = '" + truck.Height + "'");
}
querySb.Append(" WHERE truckId = " + existing.truckId + "; ");
}
try
{
string query = querySb.ToString();
command.CommandText = query;
if (query.Contains("LatestActivity ="))
command.Parameters.AddWithValue("@LatestActivity", truck.LatestActivity);
if (query.Contains("ETA ="))
command.Parameters.AddWithValue("@ETA", truck.ETA);
if (query.Contains("PerformancesUpdate ="))
command.Parameters.AddWithValue("@PerformancesUpdate", truck.PerformancesUpdate);
if (query.Contains("HeadingTo ="))
command.Parameters.AddWithValue("@HeadingTo", truck.HeadingTo);
if (query.Contains("Lat ="))
command.Parameters.AddWithValue("@Lat", truck.Lat);
if (query.Contains("Lon ="))
command.Parameters.AddWithValue("@Lon", truck.Lon);
if (query.Contains("Speed ="))
command.Parameters.AddWithValue("@Speed", truck.Speed);
command.CommandTimeout = 30;
command.CommandType = CommandType.Text;
int rows = command.ExecuteNonQuery(); //execute SQL
command.Parameters.Clear();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
connection.Close();
}
Решение: Подход Мартина помечен как решение. И, отвечая на вопрос о повышении производительности, после тестирования его подхода к локальной БД я могу сказать, что скорость обновления БД увеличивается прямо пропорционально количеству обновляемых строк в одном запросе. Например, при обновлении 300 строк мне потребовалось около 30 с вместе с запросами API. При обновлении 1000 строк все это занимает около 80-х годов. И, как уже упоминалось, единственным ограничением является количество параметров , поэтому я просто конвертирую double и DateTime в формат SQL, избавляюсь от параметров и voila .