[英]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.