EF Core 2.1 GROUP BYで各グループの最初の項目を選択する 質問する

EF Core 2.1 GROUP BYで各グループの最初の項目を選択する 質問する

トピックと投稿のリストがあるフォーラムを想像してみましょう。トピックのリストと、各トピックの最終投稿のタイトル (日付順) を取得したいと思います。

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ではLastDatesを選択できます

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

そして、.ToList() を実行すると、クエリは に正しく変換されますGROUP BY。しかし、それ以上進むことはできません。以下は、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)
            })

JOIN を試みると 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 (トピックごとに個別のクエリを実行する) を使用して実行できることはわかっていますが、それを避けたいと思います。

ベストアンサー1

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();

おすすめ記事