2013-11-13 5 views
0

У меня есть запрос, который обрабатывает около 500 записей, извлеченных из разных таблиц, сгруппированных, а затем суммированных (если это слово) в рабочий отчет. Все работает нормально, но для запуска этого отчета требуется около 30 секунд, и я получаю жалобы от своих пользователей.Ускорение запросов суммы группы linq

Процедура в вопросе это одна:

public static List<LabourEfficiencies> GetLabourEfficienciesByTimeSheet(DateTime dateFrom, DateTime dateTo) 
    { 
     CS3Entities ctx = new CS3Entities(); 

     //get all relevant timesheetline items 
     var tsItems = from ti in ctx.TimeSheetItems 
         where ti.TimeSheetHeader.Date >= dateFrom && ti.TimeSheetHeader.Date <= dateTo && ti.TimeSheetHeader.TimeSheetCategory != "NON-PROD" 
         select new TimesheetLine 
         { 
          TimesheetNo = ti.TimeSheetNo, 
          HoursProduced = ti.HoursProduced, 
          HoursProducedNet = ti.HoursProducedNet, 
          ItemID = ti.ItemID, 
          ProcessID = ti.ProcessID, 
          ProcessDuration = ti.ProcessDuration, 
          DowntimeHours = 0M 
         }; 

     //get all relevant downtimeline items 
     var tsDownT = from dt in ctx.DowntimeItems 
         where dt.TimeSheetHeader.Date >= dateFrom && dt.TimeSheetHeader.Date <= dateTo && dt.TimeSheetHeader.TimeSheetCategory != "NON-PROD" 
         select new TimesheetLine 
         { 
          TimesheetNo = dt.TimeSheetNo, 
          HoursProduced = 0M, 
          HoursProducedNet = 0M, 
          ItemID = "", 
          ProcessID = "", 
          ProcessDuration = 0M, 
          DowntimeHours = dt.DowntimeHours 
         }; 

     //combine them into single table 
     var tsCombi = tsItems.Concat(tsDownT); 

     var flatQuery = (from c in tsCombi 
         join th in ctx.TimeSheetHeaders on c.TimesheetNo equals th.TimeSheetNo 
         select new 
            { 
             th.TimeSheetNo, 
             th.EmployeeNo, 
             th.TimeSheetCategory, 
             th.Date, 
             c.HoursProduced, 
             c.ProcessDuration, 
             th.HoursWorked, 
             c.HoursProducedNet, 
             c.DowntimeHours, 
             c.ItemID 
             }); 

     //add employee details & group by timesheet no (1 line per timesheet no) 
     //NB. FnTlHrs checks whether there are any indirect hrs & deducts them if there are 
     var query = flatQuery.GroupBy(f => f.TimeSheetNo).Select(g => new LabourEfficiencies 
                      { 
                       Eno = g.FirstOrDefault().EmployeeNo, 
                       Dept =g.FirstOrDefault().TimeSheetCategory, 
                       Date = g.FirstOrDefault().Date, 
                       FnGrHrs =g.Where(w =>w.TimeSheetCategory == "FN" &&!w.ItemID.StartsWith("090")).Sum(h => h.HoursProduced), 
                       FnTlHrs =g.Where(w =>w.ItemID.StartsWith("090")).Sum(h => h.ProcessDuration) >0? (g.FirstOrDefault(w =>w.TimeSheetCategory =="FN").HoursWorked) -(g.Where(w =>w.ItemID.StartsWith("090")).Sum(h =>h.ProcessDuration)): g.FirstOrDefault(w =>w.TimeSheetCategory =="FN").HoursWorked, 
                       RmGrHrs =g.Where(w =>w.TimeSheetCategory == "RM").Sum(h => h.HoursProduced),RmGrHrsNet =g.Where(w =>w.TimeSheetCategory == "RM").Sum(h => h.HoursProducedNet), 
                       RmTlHrs =g.FirstOrDefault(w =>w.TimeSheetCategory == "RM").HoursWorked, 
                       MpGrHrs =g.Where(w =>w.TimeSheetCategory =="MATPREP").Sum(h => h.HoursProduced), 
                       MpTlHrs =g.FirstOrDefault(w =>w.TimeSheetCategory =="MATPREP").HoursWorked, 
                       DtHrs = g.Sum(s => s.DowntimeHours), 
                       Indirect =g.Where(w =>w.ItemID.StartsWith("090")).Sum(h => h.ProcessDuration) 
                      }); 

     return query.ToList(); 
    } 

Первые несколько битов просто собрать данные, это последний вопрос, который является «мясо» процедуры и занимает время.

Я уверен, что я сделал что-то ужасное, поскольку SQL, который он выплевывает, ужасен, но для жизни меня я не вижу, как его улучшить.

Любые советы приветствуются.

Гордон

+0

Ну ... Можете ли вы скопировать вставку SQL-запроса в SSMS, запустить, а затем просмотреть план выполнения и проверить, присутствуют ли все необходимые индексы? – LINQ2Vodka

+2

Если вы только вернете около 500 записей, не проще ли сделать ToList() на вашем FlatQuery и вытащить все это в память? –

+0

@ Опция RobLyndon тоже хорошо выглядит, IMO – LINQ2Vodka

ответ

1

Ваше выражение получает оптимизирован как в IQueriable компиляции и SQL оптимизации запросов сервера и даже здесь занимает так долго. Очень вероятно, что вам не нужны индексы столбцов, необходимые для выполнения плана выполнения. Скопируйте/вставьте выраженное выражение SQL в SSMS, запустите его и посмотрите фактический план. Оптимизируйте структуру базы данных при необходимости (поместите индексы). В противном случае у вас есть очень большой объем данных, что делает процесс медленным.

+0

Что вы думаете о .AsParallel() ??? –

+0

Никогда не работал с ним. Похоже, что это будет материализовать данные до их выполнения, поэтому данные сначала будут выведены из сервера. Не уверен, что это хороший вариант для этого случая. – LINQ2Vodka

+0

Джим, мне следовало бы взглянуть на индексы перед рукой, добавив один индекс, и теперь запрос занимает менее секунды в 30 раз. –

Смежные вопросы