2010-11-04 48 views
0

我試圖從SQL(T-SQL)移植到LINQ-to-Entities 4.0(C#)的查詢。結果集包含標準「詳細信息行」以及聚合「統計信息」的組合。在LINQ-to-Entities查詢中選擇記錄的聚合(分組)統計信息

原來的SQL中使用的標準選擇左右加入到聚合信息,與此類似:

SELECT 
    UserId, 
    Name, 
    Email, 
    ISNULL(Stats.TotalPosts, 0) as TotalPosts, 
    Stats.LastPost 
FROM Users 
LEFT OUTER JOIN 
(
    SELECT UserId, COUNT(*) as TotalPosts, MAX(DatePosted) as LastPost 
    FROM Articles 
    GROUP BY UserId 
) as Stats ON Stats.UserId = Users.UserID 

左連接而不是使用SELECT語句中的子查詢性能方面的原因 - 一個以上的骨料統計是返回(總帖子和最後一篇文章的日期)

我已經有一些部分成功將它轉換爲C#4.0中的LINQ到實體查詢,但我不完全知道如何加入配合集團聲明。我想我正在考慮這個SQL方面,而不是正確使用LINQ。

我取得了一些成功突破了統計到一個單獨的查詢:

var stats = 
(
    from a in entities.Articles 
    group a by a.UserId into g 
    select new 
    { 
     UserId = g.Key, 
     TotalPosts = g.Count(), 
     LastUpdated = g.Max(i => i.DatePosted) 
    } 
); 

var query = 
(
    from u in entities.Users 
    join s in stats on u.UserId equals s.UserId 
    orderby u.Name 
    select new UserListing() 
    { 
     UserId = u.UserId, 
     Name = u.Name, 
     Email = u.Email, 
     TotalPosts = s.TotalPosts, 
     LastUpdated = s.LastUpdated 
    } 
); 

不幸的是,在LINQ查詢連接使用過濾掉誰沒有提交的任何文章的所有用戶。

切換到外通過包括DefaultIfEmpty加入的當量導致其他問題 - I只能返回爲TotalPosts而不是0「空」即使「TotalPosts =(s.TotalPosts == NULL)0:S'。 TotalPosts「,除非TotalPosts屬性可以爲空,否則將引發異常。

以這種方式組合詳細信息行和聚合信息的最佳做法是什麼?

謝謝!

回答

1

試試這個:

var query = 
(
    from u in entities.Users 
    join s in stats on u.UserId equals s.UserId into g 
    from a in g.DefaultIfEmpty() 
    orderby u.Name 
    select new UserListing() 
    { 
     UserId = u.UserId, 
     Name = u.Name, 
     Email = u.Email, 
     TotalPosts = a.TotalPosts, 
     LastUpdated = a.LastUpdated 
    } 
); 
0

爲了得到一個外連接,你需要使用DefaultIfEmpty。 爲了解決這個問題,空你可以嘗試

TotalPosts = s.TotalPosts.GetValueOrDefault(), 

,或者s.TotalPosts並不會自動轉起來作爲int?你可以嘗試像你有

TotalPosts = ((int?)s.TotalPosts).GetValueOrDefault(0), 
+0

我無法得到這個編譯。我的LINQ知識不是很好,但GetValueOrDefault不是「int」或「int?」的擴展方法 – ShadowChaser 2010-11-05 00:18:23

1

一種選擇黑客是爲了確保stats查詢中的適當屬性可以爲空。如果可能的話,LINQ-to-entities將進行必要的調整以使其工作。然後照常執行左外連接。

var stats = 
(
    from a in entities.Articles 
    group a by a.UserId into g 
    select new 
    { 
     UserId = g.Key, 
     TotalPosts = (int?)g.Count(), 
     LastUpdated = g.Max(i => i.DatePosted) 
    } 
); 

var query = 
(
    from u in entities.Users 
    join s in stats on u.UserId equals s.UserId into joinedStats 
    from s in joinedStats.DefaultIfEmpty() // do left outer join 
    orderby u.Name 
    select new UserListing() 
    { 
     UserId = u.UserId, 
     Name = u.Name, 
     Email = u.Email, 
     TotalPosts = s.TotalPosts, // null if doesn't contain stats 
     LastUpdated = s.LastUpdated // default DateTime if doesn't contain stats 
    } 
); 
+0

這有幫助,但我仍然有問題的TotalPosts爲null而不是零(和要求它被鍵入爲「int?」。 有沒有什麼辦法來解決查詢中的問題,並滾動「stats 「up into the same statement? – ShadowChaser 2010-11-05 00:19:56

+0

'TotalPosts'將是'int?'的類型,如果相應的用戶沒有任何數據,它的值就是'null'。這就是你想要的嗎?當然你可以合併'stats'查詢與主要的一個,但它是更清潔你現在的方式。我不會那樣做,如果我是你。 – 2010-11-05 01:09:02

+0

@ShadowChaser:linq工作的方式沒有優勢滾動查詢在一起。不能提高性能。讓他們分開實際上更清楚。 – Hogan 2010-11-05 01:48:20