Что быстрее? Структурный массив или DataTable - PullRequest
5 голосов
/ 10 марта 2010

Я использую LinqToSQL для обработки данных с SQL Server, чтобы выгрузить их на сервер iSeries для дальнейшей обработки. Подробнее об этом здесь .

Моя проблема в том, что обработка 350 строк данных занимает около 1,25 минут. Я все еще пытаюсь расшифровать результаты с помощью SQL Server Profiler, но выполняется TON запросов. Вот немного подробнее о том, что я делаю:

using (CarteGraphDataDataContext db = new CarteGraphDataDataContext())
{
    var vehicles = from a in db.EquipmentMainGenerals
                   join b in db.EquipmentMainConditions on a.wdEquipmentMainGeneralOID equals b.wdEquipmentMainGeneralOID
                   where b.Retired == null
                   orderby a.VehicleId
                   select a;

    et = new EquipmentTable[vehicles.Count()];

    foreach (var vehicle in vehicles)
    {
       // Move data to the array

       // Rates
       GetVehcileRates(vehicle.wdEquipmentMainGeneralOID);

       // Build the costs accumulators
       GetPartsAndOilCosts(vehicle.VehicleId);
       GetAccidentAndOutRepairCosts(vehicle.wdEquipmentMainGeneralOID);

       // Last Month's Accumulators
       et[i].lastMonthActualGasOil = GetFuel(vehicle.wdEquipmentMainGeneralOID) + Convert.ToDecimal(oilCost);
       et[i].lastMonthActualParts = Convert.ToDecimal(partsCost);
       et[i].lastMonthActualLabor = GetLabor(vehicle.VehicleId);
       et[i].lastMonthActualOutRepairs = Convert.ToDecimal(outRepairCosts);
       et[i].lastMonthActualAccidentCosts = Convert.ToDecimal(accidentCosts);

       // Move more data to the array

       i++;
   }
}

Все методы Get выглядят примерно так:

private void GetPartsAndOilCosts(string vehicleKey)
{
   oilCost = 0;
   partsCost = 0;

   using (CarteGraphDataDataContext db = new CarteGraphDataDataContext())
   {
      try
      {
         var costs = from a in db.WorkOrders
                     join b in db.MaterialLogs on a.WorkOrderId equals b.WorkOrder
                     join c in db.Materials on b.wdMaterialMainGeneralOID equals c.wdMaterialMainGeneralOID
                     where (monthBeginDate.Date <= a.WOClosedDate && a.WOClosedDate <= monthEndDate.Date) && a.EquipmentID == vehicleKey
                     group b by c.Fuel into d
                     select new
                            {
                                isFuel = d.Key,
                                totalCost = d.Sum(b => b.Cost)
                            };

          foreach (var cost in costs)
          {
             if (cost.isFuel == 1)
             {
                oilCost = (double)cost.totalCost * (1 + OVERHEAD_RATE);
             }
             else
             {
                partsCost = (double)cost.totalCost * (1 + OVERHEAD_RATE);
             }
          }
       }
       catch (InvalidOperationException e)
       {
          oilCost = 0;
          partsCost = 0;
       }
    }

    return;
 }

Я думаю, что сокращение количества запросов к БД должно ускорить обработку. Если LINQ делает SELECT для каждой записи, возможно, мне нужно сначала загрузить каждую запись в память.

Я все еще считаю себя новичком в C # и ООП в целом (я в основном программирую RPG на iSeries). Так что я предполагаю, что делаю что-то глупое. Можете ли вы помочь мне исправить мою глупость (по крайней мере, с этой проблемой)?

Обновление: Думал, что вернусь и сообщу вам о том, что я обнаружил. Похоже, база данных была плохо спроектирована. Что бы ни генерировал LINQ в фоновом режиме, это был крайне неэффективный код. Я не говорю, что LINQ плох, он просто плох для этой базы данных. Я перешел на быстро скомпонованную настройку .XSD, и время обработки изменилось с 1,25 до 15 секунд. После того, как я сделаю правильный редизайн, я могу только догадываться, что побрею еще несколько секунд. Спасибо всем за ваши комментарии. Я попробую LINQ снова в другой день на лучшей базе данных.

1 Ответ

7 голосов
/ 10 марта 2010

В вашем коде я заметил несколько вещей:

  1. Вы запрашиваете базу данных несколько раз для каждого элемента в запросе «var cars». Возможно, вы захотите переписать этот запрос, чтобы потребовалось меньше запросов к базе данных.
  2. Когда вам не нужны все свойства запрашиваемой сущности или вам нужны подчиненные сущности этой сущности, для производительности лучше использовать анонимный тип в вашей select. LINQ to SQL проанализирует это и получит меньше данных из вашей базы данных. Такой выбор может выглядеть так: select new { a.VehicleId, a.Name }
  3. Запрос в GetPartsAndOilCosts можно оптимизировать, поместив вычисление cost.totalCost * (1 + OVERHEAD_RATE) в запрос LINQ. Таким образом, запрос может быть полностью выполнен в базе данных, что должно сделать его намного быстрее.
  4. Вы делаете Count() для запроса var vehicles, но вы используете его только для определения размера массива. В то время как LINQ to SQL сделает очень эффективный запрос SELECT count(*), он потребует дополнительного обхода базы данных. Кроме того (в зависимости от вашего уровня изоляции) время, когда вы начинаете выполнять итерацию запроса, может быть добавлен элемент. В этом случае ваш массив будет слишком маленьким, и будет выдан ArrayIndexOutOfBoundsException. Вы можете просто использовать .ToArray() для запроса или создать List<EquipmentTable> и вызвать .ToArray() для этого. Обычно это будет достаточно быстро, особенно если у вас в коллекции только 380 предметов, и это, безусловно, будет быстрее, чем дополнительная поездка в базу данных (количество).
  5. Как вы, вероятно, уже ожидали, количество запросов к базе данных является актуальной проблемой. Переключение между массивом struct или DataTable не будет сильно отличаться.
  6. После того, как вы оптимизировали как можно больше запросов, начните анализировать оставшиеся запросы (с помощью профилировщика SQL) и оптимизируйте эти запросы с помощью мастера настройки индекса. Он предложит вам новые индексы, которые могут значительно ускорить процесс.
<Ч />

Небольшое дополнительное объяснение пункта № 1. То, что вы делаете здесь, немного похоже на это:

var query = from x in A select something;

foreach (var row in query)
{
    var query2 = from y in data where y.Value = row.Value select something;

    foreach (var row2 in query2)
    {
        // do some computation.
    }
}

То, что вы должны попытаться выполнить, это удалить подзапрос query2, потому что он выполняется в каждой строке верхнего запроса. Таким образом, вы можете получить что-то вроде этого:

var query =
    from x in A
    from y in B
    where x.Value == y.Value
    select something;

foreach (var row in query)
{
}

Конечно, этот пример упрощен, и в реальной жизни он становится довольно сложным (как вы уже заметили). В вашем случае также потому, что у вас есть несколько из этих «подзапросов». Это может занять некоторое время, чтобы понять это правильно, особенно из-за недостатка знаний о LINQ to SQL (как вы сами сказали).

Если вы не можете понять это, вы всегда можете спросить еще раз здесь, в Stackoverflow, но, пожалуйста, не забудьте свести вашу проблему к мельчайшим возможностям, потому что неинтересно читать чей-то беспорядок (нам не платят за это) :-) Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...