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