2010-09-16 143 views
3

我有以下LINQ查詢:如何優化這個linq查詢?

var allnews = from a in db.News 
           where !(from c in db.NewsViews 
             where c.UserGuid == thisUser.UserGuid 
             select c.NewsGuid).Contains(a.NewsGuid) 
           orderby a.Date descending 
           select a; 

我想知道什麼是優化的最佳方式?或者查詢分析器會爲我做這件事嗎?

編輯:這個想法是獲得用戶還沒有看到的所有新聞項目。所以一旦用戶看到一個項目,我將這個項目存儲在NewsViews中。新聞本身在新聞中。

+1

速度慢嗎? ___ – 2010-09-16 17:23:51

+0

它實際上並不慢,但現在表中幾乎沒有數據。雖然這不是最好的方法,但是必須有一個更好的解決方案,速度更快。 – rksprst 2010-09-16 17:31:36

+3

所以擔心後來的速度..當它很重要 – corymathews 2010-09-16 18:04:18

回答

4

子查詢似乎不使用a,所以

 //untested 
     var allnews = from a in db.News 
        let excluders = from c in db.NewsViews 
            where c.UserGuid == thisUser.UserGuid 
            select c.NewsGuid 
          where !excluders.Contains(a.NewsGuid) 
          orderby a.Date descending 
          select a; 

但是請注意,您現在正在通過LINQ進行SQL優化(順便說一句,這是L2S還是EF?)。
正常的SQL優化很困難。你必須用現實的數據來衡量和分析。很可能@ Joachim的多內連接子查詢方法更好。

+0

+1注意到估計它的性能非常棘手 - 測試它! – 2010-09-16 18:52:47

+0

順便說一句,將子查詢放入let子句中並不會改變原始查詢的語義,所以如果這會改變性能以獲得更好的結果會令人驚訝 - 雖然您永遠不知道... – 2010-09-16 18:55:58

+0

@ Earnon:我假設有一些緩存,但你說得對,IEnumerable並不是那麼明顯。但這是一個IQueryable,我指望SQL服務器。 – 2010-09-16 19:01:00

2

而不是使用包含,你可以添加到您的內部查詢的WHERE語句:

...和你內心的c.newsguid == a.newsguid

和。任何()查詢

var allnews = from a in db.News 
        where !(from c in db.NewsViews 
          where c.UserGuid == thisUser.UserGuid 
          and c.NewsGuid == a.NewsGuid).Any() 
        orderby a.Date descending 
        select a; 
+0

呃,.Any()不會做你的想法... – 2010-09-16 17:42:23

+0

是的,它會返回一個集合/查詢中是否有任何項目。在子查詢中包含相等條件具有完全相同的結果,僅在子查詢中使用較小的集合,並且不使用'in'語句。 – 2010-09-16 18:09:07

+0

對,因此:'new [] {false,false} .Any()'是真的,因爲集合不是空的 - 任何布爾值都不存在的事實是不相關的。 – 2010-09-16 18:25:52

1

我假定,我們的目標就是以日期降序排列檢索NewsViews:

db.News.OrderByDescending(a => a.Date).NewsViews; 

這當然,假定您已經設置了一個協會在新聞NewsViews之間模型實體。通過提前建立關聯,子查詢就不再需要了。

UPDATE:

我一直在使用LINQ到SQL約18個月,我一直在使用相同的結構,你對我的NOT IN查詢出的一個。如前所述,如果您提前在模型中設置關聯並在數據庫本身中使用索引,您可能會遇到性能下降,但從LINQ的角度來看,我相信您的優化程度與您的「在不訴諸不必要的隱晦查詢語句的情況下獲得。

0

也許這是我缺乏linq知識,但可能是一個左連接,其中NewsViews中的列爲空?這似乎比做一個子查詢並比較兩者更好。

1

這裏有一個替代的表述:

from newsitem in db.News 
join viewing in (
     from viewing in db.NewsViews 
     where viewing.UserGuid == thisUser.UserGuid 
     select viewing 
) on newsitem.NewsGuid equals viewing.NewsGuid into usersviewings 
where !usersviewings.Any() 
orderby newsitem.Date descending 
select newsitem; 

但至於這是否是更快了 - 嗯,這是任何人的猜測;嘗試一下。從根本上說,你做了一個左連接,左邊的部分被過濾了,並且不能返回任何結果 - 這個索引不好,AFAIK。執行引擎將需要掃描新聞集中的所有行,並且如果您有SQL支持,則表掃描不是您的朋友。話雖如此,除非你確實希望這是一個巨大的表,它可能沒有多大關係,特別是如果你只報告前N個命中......

0

你可以把這裏的最好優化舉措,將允許從NewsViews導航到News ...由於不存在,我不得不通過優化來得到一點點黑客。

db.News.Join(db.News.Select(n => n.NewsGuid) 
    .Except(db.NewsViews 
     .Where(c => c.UserGuid == thisUser.UserGuid) 
     .Select(c => c.NewsGuid) 
    ), n1 => n1.NewsGuid, n2 => n2, (n1, n2) => new { n1 = n1, n2 = n2 }) 
    .Select(anon => anon.n1); 

一個除了會產生當你試圖做一個查詢,其中一個列表中不包含另一個列表的最好執行SQL。由於沒有從NewsView導航到News,我們必須用Inner Join作弊來返回新聞。

這可以做的另一種方式是我的朋友羣加入。

db.News 
    .GroupJoin(db.NewsViews, n => n.NewsGuid, nv => nv.NewsGuid, (n, nv) => new { News = n, NewsViewList = nv }) 
    .Where(anon => anon.NewsViewList != null) // I don't remember the best test here, either it's not null, or the count > 0 :-) 
    .OrderByDescending(anon => anon.News.Date) 
    .Select(anon => anon.News); 

這就是我至少會這麼做的。