web-dev-qa-db-ja.com

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

5
Shaddix

基本的に私が今やっていることは実行した後です

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

これは、SELECT * FROM Posts WHERE (Posts.TopicId == 1 AND Posts.CreatedDate = '2017-08-01') OR (Posts.TopicId == 2 AND Posts.CreatedDate = '2017-08-02') OR ...のような1つのクエリ(Nではなく)になります。

非常に効率的ではありませんが、ページあたりのトピックの数が非常に少ないため、効果的です。

1
Shaddix

EF Core 2.1では、GroupBy LINQ演算子は、ほとんどの場合、SQL GROUP BY句への変換のみをサポートします。 sum、maxなどの集計関数...

linq-groupby-translation

EF Coreで完全なサポートグループまで使用できます Dapper

1
Mohammad Akbari