2013-10-04 94 views
3

我有一個表,看起來像這樣的跟蹤,當一個人開始了一個項目:Linq查詢發現日期範圍

PersonId 
ProjectId 
StartDate 

我想使用LINQ(LINQ到實體)來獲得結果這樣設置

PersonId 
ProjectId 
StartDate 
EndDate 

在哪裏結束日期是他們的下一個項目的起始日期(在按起始日期排序)或空,如果沒有最近的一個項目。

這是我做了什麼:

context.PersonProjects.Select(pp => new { 
    pp.PersonId, 
    pp.ProjectId, 
    pp.StartDate, 
    EndDate = context.PersonProjects.Where(pp2 => pp2.PersonId == pp.PersonId && pp2.StartDate > pp.StartDate).OrderBy(pp2 => pp2.StartDate).Select(pp2 => pp2.StartDate).FirstOrDefault() 
}) 

是否有這樣做的更好的性能呢?

回答

1

這樣做的一種更高性能(但不是清晰)的方法是將所有內容都讀入開始日期排序的列表中,然後遍歷列表,並抓取以下項目的開始日期(如果有的話) :

// You need a named class in order to make this work 
class PersonProject { 
    int PersonId {get;set;} 
    int ProjectId {get;set;} 
    DateTime StartDate {get;set;} 
    DateTime EndDate {get;set;} 
} 
... 
// Run your query, and put the results in a list 
var listOfProjects = context 
    .PersonProjects 
    .OrderBy(pp => pp.StartDate) 
    .Select(pp => new PersonProject { 
     PersonId = pp.PersonId, 
     ProjectId = pp.ProjectId, 
     StartDate = pp.StartDate 
    }).ToList(); 
// Now walk through the list, setting the end time to the start of the next item 
for (int i = 0 ; i < listOfProjects.Length-1 ; i++) { 
    listOfProjects[i].EndDate = listOfProjects[i+1].StartDate; 
} 

查詢和用於此解決方案的「修復了」部分都是線性的,因此性能將是作爲讀取PersonProject表本身一樣好。

+0

謝謝,這是有道理的。這個特定的表格有大約300K行,所以我不確定將它們全部加載到內存中。通常,我只想要這些結果的總和(例如,查找在一段時間內從事項目工作的人數),這意味着這僅僅是另一個查詢的基本查詢,所以它似乎是理想的保存它IQuerya ble – Adam

+0

@Adam如果你把「修復循環」放在一個單獨的方法中,它需要一個正確排序的列表,你可以保留'IQueryable',並且只在你想要的子集在內存中時修復部分。 – dasblinkenlight

+0

是的,我只是不能使用「EndDate」來過濾結果,直到修復循環之後。有時候我會想。我可能不得不做一些測試,看看這是如何比較。 – Adam