2017-05-05 45 views
1

很慢我正在這樣的代碼:Enumerable.Any()是大LINQ的數據集

var somethings = db.Somethings.Select(s => new SomethingViewModel 
{ 
    Id = s.Id, 
    Name = s.Name, 
    IsActive = s.IsActive 
    SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel 
    { 
     Id = ss.Id, 
     Name = ss.Name, 
     IsActive = ss.IsActive 
    }).Where(wss => wss.IsActive)       
}).Where(ws => ws.IsActive && (ws.SubSomethings.Any())) //remove elements if no SubSomethings 
.ToList(); 

正如你可以看到,這是一個一對多的關係。有一些SubSomethings的東西列表。如果我拿出& &(ws.SubSomethings.Any()),我會得到一個非常快速的返回列表。

但是,我只想在列表中包含至少有一個SubSomething的東西。我也試過以下,並得到了相同的可怕的效率:

var somethings = db.Somethings.Select(s => new SomethingViewModel 
{ 
    Id = s.Id, 
    Name = s.Name, 
    IsActive = s.IsActive 
    SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel 
    { 
     Id = ss.Id, 
     Name = ss.Name, 
     IsActive = ss.IsActive 
    }).Where(wss => wss.IsActive)       
}).Where(ws => ws.IsActive) 
.ToList(); //this finishes very quickly 

var somethings2 = somethings.Where(s => s.SubSomethings.Any()).ToList(); //This is where the code bogged down 

我怎樣才能重新寫我的查詢來獲取陷入泥淖代碼要快很多?有一件事要注意:這對一個或兩個記錄來說工作得很好。當我點擊> 8000條記錄時,至少需要四分鐘。

下面是我對SomethingId,其對應的外鍵的SubSomething表中創建的索引Something.Id

CREATE NONCLUSTERED INDEX [IX_SubSomething_SomethingId] ON [dbo].[SubSomething] 
(
    [SomethingId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
GO 

這裏是SubSomething.SomethingId的創建外鍵:

ALTER TABLE [dbo].[SubSomething] WITH CHECK ADD CONSTRAINT [FK_SubSomething_Something_SomethingId] FOREIGN KEY([SomethingId]) 
REFERENCES [dbo].[Something] ([Id]) 
GO 

ALTER TABLE [dbo].[SubSomething] CHECK CONSTRAINT [FK_SubSomething_Something_SomethingId] 
GO 
+0

它應該給你相同的結果,但要嘗試不會傷害,如果使用'ws.SubSomethings.Count()> 0'代替'ws.SubSomethings.Any()'會發生什麼? – Gusman

+2

@Gusman - 由於Count需要消耗整個數據,所以速度會更慢 –

+0

@Gusman - 如果有的話,Count()方法會更慢,因爲它需要迭代所有項目以獲得計數是特定集合類型的一些捷徑,但不是全部)。 'Any()'只需要一個元素。 – DigiFriend

回答

6

EF核心是你的問題。目前已知在查詢包含子集合投影時執行N + 1個子查詢。

只有這樣,才能解決辦法,並限制了整個事情到2個SQL查詢是過濾你一樣可以,然後加載整個實體使用預先加載在內存子實體設置,然後切換到LINQ到對象和做最後的投影/過濾:

var somethings = db.Somethings 
    .Include(s => s.SubSomethings) 
    .Where(s => s.IsActive) 
    .AsEnumerable() 
    .Select(s => new SomethingViewModel 
    { 
     Id = s.Id, 
     Name = s.Name, 
     IsActive = s.IsActive, 
     SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel 
     { 
      Id = ss.Id, 
      Name = ss.Name, 
      IsActive = ss.IsActive 
     }).Where(wss => wss.IsActive).ToList() 
    }) 
    .Where(s => s.SubSomethings.Any()) 
    .ToList(); 
0

我認爲問題在於你正在將第一個查詢轉換爲列表。試試這個:

var products = db.Products.Where(ws => ws.IsActive && ws.SubSomethings.Count(ss => ss.IsActive) > 0) 
.Select(p => new SomethingViewModel 
{ 
    Id = p.Id, 
    Name = p.Name, 
    IsActive = p.IsActive, 
    SubSomethings = p.SubSomethings.Select(ss => new PartViewModel 
    { 
     Id = ss.Id, 
     Name = ss.Name, 
     IsActive = ss.IsActive 
    }).Where(wss => wss.IsActive) 
}).ToList(); 
+2

這只是取代OP第一次嘗試的OP第二次嘗試,不是嗎? – hvd

+0

是的。我看不出有什麼區別。 – crackedcornjimmy

+1

是的。我給了這個第二個想法,我認爲在你返回結果之前你不需要對這些字段進行投影。我正在修復。 – JuanR

0

我相信每個SomethingSubSomethings屬性剩餘可枚舉,其底層數據庫查詢到的代碼的最後一行不執行。因此,在這一行中,您將爲每個Something順序執行1個數據庫查詢。在初始查詢SubSomethings使你的所有數據通過內部獲取的一起

我建議的包括()連接:

var somethings = db.Somethings 
    .Include("SubSomethings") // added this 
    .Select(s => new SomethingViewModel 
{ 
    Id = s.Id, 
    Name = s.Name, 
    IsActive = s.IsActive 
    SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel 
    { 
     Id = ss.Id, 
     Name = ss.Name, 
     IsActive = ss.IsActive 
    }).Where(wss => wss.IsActive)       
}).Where(ws => ws.IsActive) 
.ToList(); //this finishes very quickly 

var somethings2 = somethings.Where(s => s.SubSomethings.Any()).ToList(); //This is where the code bogged down 
+0

你能舉個例子嗎?已添加代碼 – crackedcornjimmy

+0

。如果這沒有辦法,你可以在分配給SubSomethings屬性的時候另外做一個ToList()。 – finrod

0

正如伊萬Stoev說,EF核心是你的問題,我同意一個替代不使用LINQ到SQL但生病mutch更好的性能:

  1. 創建視圖在數據庫中加入邏輯來加入你需要的數據
  2. 使用db.Database.SqlQuery( 「查詢」)來查詢視圖以及返回數據:

    變種結果= db.Database.SqlQuery < SomethingViewModel>( 「選擇可樂,COLB,COLC從ViewSomething」)

    對象SomethingViewModel必須是一個強類型,其中propreties與視圖的cols匹配。

我知道這並不美觀,並且對視圖有硬編碼查詢,但通常是自從您通過編譯器以來使用EF獲得的最快性能。