簡體   English   中英

EF Core 2.1 GROUP BY 和 select 每個組中的第一項

[英]EF Core 2.1 GROUP BY and select first item in each group

讓我們想象一個論壇,其中包含主題和帖子的列表。 我想獲取每個主題的主題列表和最后一篇文章的標題(按日期)。

有沒有辦法使用 EF Core (2.1) 來實現這一點? 在 SQL 中可以這樣做

SELECT Posts.Title, Posts.CreatedDate, Posts.TopicId FROM 
  (SELECT Max(CreatedDate), TopicId FROM Posts GROUP BY TopicId) lastPosts
JOIN Posts ON Posts.CreatedDate = lastPosts.CreatedDate AND Posts.TopicId = lastPosts.TopicId

在 EFCore 我可以 select LastDates

_context.Posts.GroupBy(x => x.TopicId, (x, y) => new
            {
                CreatedDate = y.Max(z => z.CreatedDate),
                TopicId = x,
            });

如果我 run.ToList() 查詢被正確翻譯為GROUP BY 但我不能進一步 go 。 以下在 memory 中執行,而不是在 SQL 中執行(導致SELECT * FROM Posts ):

            .GroupBy(...)
            .Select(x => new
            {
                x.TopicId,
                Post = x.Posts.Where(z => z.CreatedDate == x.CreatedDate)
                //Post = x.Posts.FirstOrDefault(z => z.CreatedDate == x.CreatedDate)
            })

嘗試加入會給出 NotSupportedException(無法解析表達式):

.GroupBy(...)
.Join(_context.Posts,
                    (x, y) => x.TopicId == y.TopicId && x.CreatedDate == y.CreatedDate,
                    (x, post) => new
                    {
                        post.Title,
                        post.CreatedDate,
                    })

我知道我可以使用 SELECT N+1 來做到這一點(每個主題運行一個單獨的查詢),但我想避免這種情況。

基本上我現在正在做的是跑步后

var topics = _context.Posts.GroupBy(x => x.TopicId, (x, y) => new
            {
                CreatedDate = y.Max(z => z.CreatedDate),
                TopicId = x,
            }).ToList();

我構建以下查詢:

Expression<Func<Post, bool>> lastPostsQuery = post => false;
foreach (var topic in topics) 
{
    lastPostsQuery = lastPostsQuery.Or(post => post.TopicId == topic.TopicId && post.CreatedDate = topic.CreatedDate); //.Or is implemented in PredicateBuilder
}
var lastPosts = _context.Posts.Where(lastPostsQuery).ToList();

這會導致一個查詢(而不是 N),如SELECT * FROM Posts WHERE (Posts.TopicId == 1 AND Posts.CreatedDate = '2017-08-01') OR (Posts.TopicId == 2 AND Posts.CreatedDate = '2017-08-02') OR ... .

效率不是很高,但由於每頁的主題數量很少,因此可以解決問題。

我不知道從哪個版本的 EFCore 開始是可能的,但現在有一個更簡單的單查詢替代方案:

context.Topic
   .SelectMany(topic => topic.Posts.OrderByDescending(z => z.CreatedDate).Take(1),
        (topic, post) => new {topic.Id, topic.Title, post.Text, post.CreatedDate})
   .OrderByDescending(x => x.CreatedDate)
   .ToList();

在 EF Core 2.1 GroupBy LINQ 運算符中,大多數情況下僅支持轉換為 SQL GROUP BY 子句。 聚合函數,如 sum、max ...

linq-groupby-translation

您可以在 EF Core 中使用Dapper 的完整支持組

我不確定 EFCore 的版本是否可行,但您可以嘗試以下操作:它將首先分組 select 最大 ID 並從每個組返回最大 ID 記錄。

var firstProducts = Context.Posts
.GroupBy(p => p.TopicId)
.Select(g => g.OrderByDescending(p => p.id).FirstOrDefault())
.ToList();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM