簡體   English   中英

優化非常大的LINQ查詢

[英]Optimizing a very large LINQ query

我需要編寫一個/多個LINQ查詢來獲取一些我將為報告顯示的數據。

我需要使用的表是StaffingResources,StaffingForecastEvents(將StaffingResource鏈接到項目)和StaffingForecasts(鏈接到StaffingForecastEvents並包含每周的工作時間)。 每個StaffingResource可以具有0個StaffingForecastEvent,而StaffingForecastEvent可以具有0個StaffingForecast。

我需要編寫LINQ查詢,對於每個資源,將包含它們具有ForecastEvent的所有項目,對於每個項目,將包含給定日期范圍(12周或6個月)的所有預測。 這是我到目前為止的內容,運行速度很慢。

// Get date ranges
var dates = new List<DateTime>();
var startDate = range == (int)RangeTypes.WEEKLY 
    ? DateTime.Today.AddDays(DayOfWeek.Monday - DateTime.Today.DayOfWeek) 
    : new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
for (var i = 0; i < (range == (int)RangeTypes.WEEKLY ? 4 : 6); i++)
{
    dates.Add(range == (int)RangeTypes.WEEKLY ? startDate.AddDays(i * 7) : startDate.AddMonths(i));
}
var endDate = dates[dates.Count-1];

// Get resources
var resources = from r in context.StaffingResourceDatas
                where r.EmployeeId.HasValue
                   && (resourceIds.Count == 0 || resourceIds.Contains(r.EmployeeId.Value))
                   && (resourceDivisions.Count == 0 || resourceDivisions.Contains(r.ResourceDivisionId))
                   && (resourceTitles.Count == 0 || resourceTitles.Contains(r.ResourceTitleId))
                   && (resourceLocations.Count == 0 || resourceLocations.Contains(r.ResourceLocationId))
                   && (supervisors.Count == 0 || supervisors.Contains(r.ReportsToId))
                   && (showAllResources || (!showAllResources && !exclusionList.Contains(r.ResourceTitleId)))
                join fe in context.StaffingForecastEvents
                   .Include(x => x.StaffingForecasts)
                   .Include(x => x.StaffingUser)
                   .Include(x => x.StaffingUser1)
                   .Include(x => x.StaffingForecasts.Select(y => y.StaffingUser))
                   .Include(x => x.StaffingForecasts.Select(y => y.StaffingUser1))
                 on r.ResourceId equals fe.ResourceId into g1
                from fe in g1.DefaultIfEmpty()
                join p in context.StaffingProjectDatas on fe.JobNumber equals p.JobNumber into g2
                from p in g2.DefaultIfEmpty()
                group new { ForecastEvent = fe, Project = p } by r into g3
                select new
                {
                    ResourceId = g3.Key.ResourceId,
                    Name = g3.Key.ResourceName,
                    Title = g3.Key.ResourceTitle,
                    Division = g3.Key.ResourceDivision,
                    Location = g3.Key.ResourceLocation,
                    AvailableDate = g3.Key.AvailableDate,
                    SupervisorEmail = g3.Key.ManagerEmail,
                    Projects = g3.Where(p => p.ForecastEvent != null).Select(p => new
                    {
                        JobNumber = p.ForecastEvent.JobNumber,
                        Description = p.Project.ProjectDescription,
                        Name = p.Project.ProjectName,
                        Division = p.Project.ProjectDivision,
                        ProjectManager = p.Project.PMName,
                        Notes = p.ForecastEvent.Notes,
                        LogDate = p.ForecastEvent.LogDate,
                        LogUser = p.ForecastEvent.StaffingUser.Name,
                        AckDate = p.ForecastEvent.AcknowledgeDate,
                        AckUser = p.ForecastEvent.StaffingUser1 != null ? p.ForecastEvent.StaffingUser1.Name : null,
                        Usages = dates.Select(d => new
                        {
                            Date = d,
                            Hours = (range == (int)RangeTypes.WEEKLY)
                               ? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any() ? p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Sum(f => f.Hours) : 0)
                               : (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any() ? p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Sum(f => f.Hours) : 0),
                            LogDate = (range == (int)RangeTypes.WEEKLY)
                                // Get acknowledge or log date for week
                               ? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any()
                                   ? ((p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate) != null)
                                       ? (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate)
                                       : (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).LogDate)
                                   : null)
                                // Get acknowledge or log date for most recent forecast for month
                               : (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any()
                                   ? ((p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate) != null)
                                       ? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate)
                                       : (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Max(f => f.LogDate))
                                   : null),
                            LogUser = (range == (int)RangeTypes.WEEKLY)
                                // Get acknowledge or log user for week
                               ? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any()
                                   ? ((p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate) != null)
                                       ? (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).StaffingUser1.Name)
                                       : (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).StaffingUser.Name)
                                   : null)
                                // Get acknowledge or log user for most recent forecast for month
                               : (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any()
                                   ? ((p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate) != null)
                                       ? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().StaffingUser1.Name)
                                       : (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().StaffingUser.Name)
                                   : null),
                        })
                    })
                };

我還需要獲取每個日期范圍內每種資源的總計。

我覺得瓶頸可能是遍歷日期時所有“哪里”的瓶頸,但我不知道該怎么辦。 有任何想法嗎?

首先,您可以使用IQueryable<T>將其拆分為多個表達式。 它還可以幫助您簡化where子句。 看起來像:

var queryable = context.StaffingResourceDatas.Where(r => r.EmployeeId.HasValue);
if(resourceIds.Any())
{
    queryable = queryable.Where(r => resourceIds.Contains(r.EmployeeId.Value))
}

請注意,僅當resourceIds不為空時,這才會生成與resourceIds過濾有關的SQL,從而潛在地節省了在生成的查詢本身中檢查它的開銷。

您可以與其余過濾器類似地編寫其余過濾器。 還要注意,只有在調用ToList()查詢才會執行。 因此,您可以繼續添加任意多的子句,直到構造完成。

但是到了最后,您可能要考慮用原始SQL編寫此代碼,因為它只是巨大的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM