2015-11-03 92 views
2

我有一個LINQ查詢,我在優化時遇到了麻煩,需要大約5.5秒的時間才能運行。我使用名爲StaffingResourceData的視圖和名爲StaffingForecasts的表。優化緩慢的LINQ查詢

每個StaffingResource都有一個ResourceId,一個Division和一個Type。 StaffingForecast具有ResourceId,Project,Date(表示一週的星期一),Hours。 StaffingResource可以有0多個StaffingForecasts。

對於每個StaffingResource,我需要一個未來12周的總預測小時數列表。這是我現在所擁有的:

// Get list of dates 
var dates = new List<DateTime>(); 
var start = Utilities.GetStartOfWeek(DateTime.Today); 
for (var i = 0; i < 12; i++) 
{ 
    dates.Add(start.AddDays(i * 7)); 
} 
var end = dates[11]; 

// Get resources 
var resources = (from r in context.StaffingResourceDatas 
       where r.EmployeeId != null 
        && !exclusionList.Contains(r.ResourceTitleId) 
       join f in context.StaffingForecasts.Where(x => x.Date >= start && x.Date <= end) on r.ResourceId equals f.ResourceId into g1 
       from f in g1.DefaultIfEmpty() 
       group new { f.Date, f.Hours } by r into g2 
       select new ChartResourceModel 
       { 
        ResourceId = g2.Key.ResourceId, 
        Division = g2.Key.ResourceDivision, 
        Type = g2.Key.ResourceType, 
        Dates = dates.Select(d => new ChartDateModel 
        { 
         Date = d, 
         Available = (g2.Where(f => f.Date == d).Any() ? g2.Where(f => f.Date == d).Sum(f => f.Hours) : 0) < 24 
        }).ToList() 
       }) 
       .ToList(); 

關於如何加快速度的任何想法?

+4

[CodeReview.SE]會更好嗎? –

+0

那麼,如果沒有真正回答你的問題,你可以使用'Where(f => f.Date == d).Any()'來替代'Any'的重載'Func':'Any(f => f .Date == d)' – HimBromBeere

+2

看看正在生成的SQL,至少會給你一個啓動。 – DavidG

回答

1

玩了一段時間後,我能夠將加載時間從5.5秒縮短到1.5秒。以下是我想出了:

// Get resources 
var resources = (from r in 
        (from r in context.StaffingResourceDatas 
        where r.EmployeeId != null 
         && !exclusionList.Contains(r.ResourceTitleId) 
        join f in context.StaffingForecasts on r.ResourceId equals f.ResourceId 
        group f by r into g 
        select new 
        { 
         Resource = g.Key, 
         Forecasts = g.Where(f => f.Date >= start && f.Date <= end && f.StaffingPotentialProject == null).ToList() 
        }).ToList() 
       group r.Forecasts by r.Resource into g 
       select new ChartResourceModel 
       { 
        ResourceId = g.Key.ReportsToId, 
        Division = g.Key.ResourceDivision, 
        Type = g.Key.ResourceType, 
        Dates = dates.Select(d => new ChartDateModel 
        { 
         Date = d, 
         Available = (g.SelectMany(f => f.Where(x => x.Date == d)).Sum(x => x.Hours)) < 24 
        }).ToList() 
       }).ToList(); 

這似乎是最好的辦法是隻得到你需要的所有數據,並要求.ToList(),但不嘗試做任何幻想,然後進行任何額外的操作上數據。

0

首先,儘量避免使用「.ToList()」,除非一切都完成了,因爲當你觸發「.ToList()」時,結果正在物化,如果存在大量數據並且您希望對該數據執行更多查詢操作。

因此,請嘗試使用IQueryable屬性,這樣您至少可以更快地獲取數據,然後對其執行一些操作。

當然,請檢查發送給SQL的查詢/查詢的內容。也許您正在搜索的列沒有正確編制索引(!?),或者表中沒有任何索引(?)。

0
  • 避免.ToList(),這個不斷擴大或使用相關實體
  • CompiledQueries編譯查詢時提前,避免一些開銷(不要太多)
  • 調試您的查詢(例如通常會創建一個SQL查詢。使用http://miniprofiler.com/
3
  1. 避免使用Contains。它嚴重降低了性能。 See this post

  2. ToList()是執行查詢的命令。直到你調用ToList()方法,linq查詢不會開始,因爲linq有一個叫做的延遲執行的功能。所以如果你打電話給ToList(),你可以用Databaseof文件開始一些真正的操作。

  3. 減少表中的列降低帶寬要求(刪除你的查詢不必要的列)
  4. 關閉修訂跟蹤和身份管理(例如,ObjectTrackingEnabled在LINQ到SQL)

    using (YourDataContext dataContext = new YourDataContext())  
    { 
        dataContext.ObjectTrackingEnabled = false;  
        //Your code 
    } 
    
  5. 使用預先編譯的查詢。它有時會降低預處理開銷

+0

我有一個類似的性能問題,也包含.....有一件事是避免它或至少使用包含自身之前的所有可能和部分if減少呼叫頻率的頻率。 – Thomas

0

我願意把我的賭注押在一個簡單的子查詢,「自然」,代表所需要的信息,這樣

var query = 
    from r in context.StaffingResourceDatas 
    where r.EmployeeId != null && !exclusionList.Contains(r.ResourceTitleId) 
    select new ChartResourceModel 
    { 
     ResourceId = r.ResourceId, 
     Division = r.ResourceDivision, 
     Type = r.ResourceType, 
     Dates = dates.Select(d => new ChartDateModel 
     { 
      Date = d, 
      Available = context.StaffingForecasts.Where(f => 
       f.ResourceId == r.ResourceId && f.Date == d).Sum(f => f.Hours) < 24 
     }).ToList() 
    }; 
var sqlQuery = query.ToString(); 
var result = query.ToList();