2010-10-31 86 views
0

我們有2個表(tblSerials和tblRequests)。問題是我們該如何讓這個代碼更快地執行?正如你可以看到「從ctx.tblRequests中的請求」部分非常相似。其他更好的解決方案?嵌套Linq性能

var list = from s in ctx.tblSerials 
     orderby s.CreateDate descending 
     select new 
     { 
     SerialGUID = s.SerialGUID, 
     CreateDate = s.CreateDate, 
     NumberOfAllRequests = 
      (from request in ctx.tblRequests 
       where request.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower()) 
       select request.RequestGUID).Count(), 
     NumberOfAllRequestsDistinctByMachine = 
      (from request in ctx.tblRequests 
       where request.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower()) 
       select request.MachineCode).Distinct().Count(),      
     }; 
+0

這些表通過ADO.NET或SQL表的內存表嗎? – 2010-10-31 21:29:40

+0

它們是SQL表。是否可以使用存儲過程來處理它? – Babak 2010-11-01 04:47:06

回答

1

如果用tblRequests加入,你可以改變你的選擇到這一點:

select new 
{ 
    SerialGUID = s.SerialGUID, 
    CreateDate = s.CreateDate, 
    RequestGUID = request.RequestGUID, 
    MachineCode = request.MachineCode 
}; 

然後,你有一個單一查詢。檢索這些結果後,您可以「手動」將它們聚合到結果集上,並自己爲您做Count()Distinct().Count()

1

我不確定它是否足夠聰明以使用嵌套查詢創建連接,您可以嘗試使用連接來查看是否有幫助。

像這樣:

var requestsByMachine = (from request in ctx.tblRequests 
          group request by request.MachineCode into g 
          select new { serial = request.SerialGuid, amount = g.Distinct().Count() }).ToArray(); 

    var requestsByGuid = (from request in ctx.tblRequests 
         group request by request.requestGUID into g 
         select new { serial = request.SerialGuid, amount = g.Count()}).ToArray(); 

    var list = from s in ctx.tblSerials.ToArray() 
       join r1 in requestsByGuid on s.SerialGUID equals r1.serial 
       join r2 in requestsByMachine on s.SerialGUID equals r2.serial 
       orderby s.CreateDate descending 
       select new 
       { 
        SerialGUID = s.SerialGUID, 
        CreateDate = s.CreateDate, 
        NumberOfAllRequests = r1.amount, 
        NumberOfAllRequestsDistinctByMachine = r2.amount 
       }; 

不能嘗試它自己的時刻,但你可以在SQL事件探查器查看是否創建一個查詢。

編輯:在內存中修改

+0

我認爲它是相同的,或者可能會慢一點。 – Babak 2010-11-01 04:48:59

+0

在這種情況下,你可以通過添加ToArray強制它在內存中執行它,我會改變我的例子。它可能仍然只是在創建計數查詢時遇到問題,但是你應該能夠在sql profiler – Doggett 2010-11-01 07:17:31

+0

中看到這個代碼將生成3個查詢來檢索所有數據並將其填充到數組中。之後,系統將在客戶端內存上查詢它。 – 2010-11-01 07:28:03

1

查詢最簡單的方法,使這些快速將帶來數據到內存中。但是,即使查詢結果很可能適合內存,源數據也可能不會。所以訣竅是限制加載到內存中的數據,但在數據庫上處理where子句,但在內存中執行join子句。

在這種情況下,雖然沒有where條款推到數據庫中,所以我會假設整個tblSerials & tblRequests將裝入內存。

因此,這裏是做什麼:

var serials = 
    ctx.tblSerials.ToArray(); 

var requests = 
    ctx.tblRequests.ToArray(); 

var list = 
    from s in serials 
    orderby s.CreateDate descending 
    let rs = 
     (from request in requests 
     where request.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower()) 
     select request).ToArray() 
    let NumberOfAllRequests = 
     rs.Count() 
    let NumberOfAllRequestsDistinctByMachine = 
     rs.Select(r => r.MachineCode).Distinct().Count() 
    select new 
    { 
     s.SerialGUID, 
     s.CreateDate, 
     NumberOfAllRequests, 
     NumberOfAllRequestsDistinctByMachine, 
    }; 

這應該是做您的查詢的最快方法 - 前提是它可以在加載到內存中。

2

如果你不關心看到有0計數tblRequests您可以使用此查詢的結果:如果你確實需要這些結果,你可以輕鬆地將一個表到內存

tblSerials 
    .Join(
     tblRequests, 
     x => x.SerialGUID.ToLower(), 
     x => x.SerialGUID.ToLower(), 
     (o,i) => new { Serial = o, Request = i } 
    ).GroupBy(x => x.Serial) 
    .Select(x => new { 
     SerialGUID = x.Key.SerialGUID, 
     CreateDate = x.Key.CreateDate, 
     NumberOfAllRequests = x.Select(y => y.Request.RequestGUID).Count(), 
     NumberOfAllRequestsDistinctByMachine = x.Select(y => y.Request.MachineCode).Distinct().Count() 
    }).OrderByDescending(x => x.CreateDate); 

所以你可以使用一個多行lambda表達式:

tblSerials 
    .ToList().Select(x => { 
    var requests = tblRequests.Where(y => y.SerialGUID.ToLower().Equals(x.SerialGUID.ToLower())); 
    return new { 
       SerialGUID = x.SerialGUID, 
       CreateDate = x.CreateDate, 
       NumberOfAllRequests = requests.Count(), 
       NumberOfAllRequestsDistinctByMachine = requests.Select(y => y.MachineCode).Distinct().Count() 
      }; 
    }).OrderByDescending(x => x.CreateDate); 

或者,如果tblSerials太大,你可以使用一個外部聯接:

(from s in tblSerials 
from r in tblRequests 
    .Where(x => x.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower())) 
    .DefaultIfEmpty() 
select new { Serial = s, Request = r }) 
.GroupBy(x => x.Serial) 
    .Select(x => new { 
     SerialGUID = x.Key.SerialGUID, 
     CreateDate = x.Key.CreateDate, 
     NumberOfAllRequests = x.Any(y => y.Request != null) ? x.Select(y => y.Request.RequestGUID).Count() : 0, 
     NumberOfAllRequestsDistinctByMachine = x.Any(y => y.Request != null) ? x.Select(y => y.Request.MachineCode).Distinct().Count() : 0 
    }).OrderByDescending(x => x.CreateDate); 

注意:我實際上沒有任何性能比較,也不能保證其中的任何一個都比您的速度更快,它們只是建議。

(如果你有linqpad安裝,你可以比較一下所有這些查詢,並在下面的腳本中一些虛擬數據自己的結果:

var tblSerials = new [] { 
new { CreateDate = DateTime.Today.AddDays(-2), SerialGUID = "foo" }, 
new { CreateDate = DateTime.Today.AddDays(-3), SerialGUID = "bar" }, 
new { CreateDate = DateTime.Today.AddDays(-2), SerialGUID = "foobar" }, 
new { CreateDate = DateTime.Today.AddDays(-1), SerialGUID = "foo" } 
}; 

var tblRequests = new [] { 
new { SerialGUID = "foo", RequestGUID = "hi", MachineCode = "1" }, 
new { SerialGUID = "bar", RequestGUID = "yo", MachineCode = "2" }, 
new { SerialGUID = "foo", RequestGUID = "hello", MachineCode = "1" }, 
new { SerialGUID = "baz", RequestGUID = "yeah", MachineCode = "3" } 
}; 

tblSerials 
    .Join(
     tblRequests, 
     x => x.SerialGUID.ToLower(), 
     x => x.SerialGUID.ToLower(), 
     (o,i) => new { Serial = o, Request = i } 
    ).GroupBy(x => x.Serial) 
    .Select(x => new { 
     SerialGUID = x.Key.SerialGUID, 
     CreateDate = x.Key.CreateDate, 
     NumberOfAllRequests = x.Select(y => y.Request.RequestGUID).Count(), 
     NumberOfAllRequestsDistinctByMachine = x.Select(y => y.Request.MachineCode).Distinct().Count() 
    }).OrderByDescending(x => x.CreateDate).Dump(); 

(from s in tblSerials 
from r in tblRequests 
    .Where(x => x.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower())) 
    .DefaultIfEmpty() 
select new { Serial = s, Request = r }) 
.GroupBy(x => x.Serial) 
    .Select(x => new { 
     SerialGUID = x.Key.SerialGUID, 
     CreateDate = x.Key.CreateDate, 
     NumberOfAllRequests = x.Any(y => y.Request != null) ? x.Select(y => y.Request.RequestGUID).Count() : 0, 
     NumberOfAllRequestsDistinctByMachine = x.Any(y => y.Request != null) ? x.Select(y => y.Request.MachineCode).Distinct().Count() : 0 
    }).OrderByDescending(x => x.CreateDate).Dump(); 

tblSerials 
    .ToList().Select(x => { 
    var requests = tblRequests.Where(y => y.SerialGUID.ToLower().Equals(x.SerialGUID.ToLower())); 
    return new { 
       SerialGUID = x.SerialGUID, 
       CreateDate = x.CreateDate, 
       NumberOfAllRequests = requests.Count(), 
       NumberOfAllRequestsDistinctByMachine = requests.Select(y => y.MachineCode).Distinct().Count() 
      }; 
    }).OrderByDescending(x => x.CreateDate).Dump(); 

var list = from s in tblSerials 
     orderby s.CreateDate descending 
     select new 
     { 
     SerialGUID = s.SerialGUID, 
     CreateDate = s.CreateDate, 
     NumberOfAllRequests = 
      (from request in tblRequests 
       where request.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower()) 
       select request.RequestGUID).Count(), 
     NumberOfAllRequestsDistinctByMachine = 
      (from request in tblRequests 
       where request.SerialGUID.ToLower().Equals(s.SerialGUID.ToLower()) 
       select request.MachineCode).Distinct().Count(),      
     }; 

list.Dump(); 
2

我的經驗是,LINQ到SQL總是意味着一個大性能受到影響,因此考慮在數據庫服務器上使用存儲過程的選項,並將結果轉換爲帶有ToList()的新List(可能取決於您使用的方法)可查詢結果。

2

如果性能是您的目標,那麼查詢計劃通過SQL Ser生成了什麼看起來像什麼? this article的第五步爲如何查找查詢計劃提供了一些幫助。在接受其他答案中的某些更改後,可能會發現由於L2Sql而導致放緩不會是因爲不恰當的optomized數據庫結構。

例如,是SerialGuid索引?如果表中有足夠的條目,則獲取查詢以搜索索引可以提供一個數量級的加速。