如何对记录进行分组并仅检索具有前N个记录的第一个组

时间:2018-02-16 20:27:35

标签: c# entity-framework linq entity-framework-core ef-core-2.0

我有以下记录集

ID          BatchID     ClientName           CreatedDateTime
----------- -------------- --------------- -----------------------
1           NULL           B             2018-02-16 19:07:46.320
2           NULL           B             2018-02-16 19:07:46.320
3           NULL           B             2018-02-16 19:07:46.597
4           NULL           B             2018-02-16 19:07:46.597
5           NULL           B             2018-02-16 19:10:10.260
6           NULL           B             2018-02-16 19:10:10.260
7           NULL           B             2018-02-16 19:21:34.303
8           NULL           B             2018-02-16 19:21:34.303
9           NULL           B             2018-02-16 19:21:44.780
10          NULL           B             2018-02-16 19:21:44.780
11          NULL           A             2018-02-16 19:24:35.623
12          NULL           A             2018-02-16 19:24:35.623
13          NULL           A             2018-02-16 19:24:42.867
14          NULL           A             2018-02-16 19:24:42.867

我在EF Core中使用LINQ to SQL。

我想过滤BatchIDNULL的记录,然后按CreatedDateTime对已过滤的记录进行排序,然后按ClientName对其进行分组,然后记录前5条记录< strong>来自第一个组。

根据上面给定的记录集,它应返回ClientName B的Ids 1,2,3,4,5的记录

所以这是我的查询

 var result = await _DBContext.BatchRequests
                .Where(x => x.BatchID.HasValue == false)
                .OrderBy(x => x.CreatedDateTime)
                .GroupBy(x => x.ClientName)
                .FirstAsync();

问题
  1 GT;查询返回客户A
  2 - ;我如何只拍摄前5个记录

更新1

Sql Profiler显示以下内容,它甚至不在SQL中分组

SELECT [x].[ID], [x].[BatchID], [x].[ClientName], [x].[CreatedDateTime]
FROM [BatchRequests] AS [x]
WHERE CASE
    WHEN [x].[BatchID] IS NULL
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END <> 0
ORDER BY [x].[ClientName]

2 个答案:

答案 0 :(得分:4)

首先,如果在OrderBy实现中将GroupBy跟随,将LINQ查询转换为SQL,通常Queryable无效(被忽略)。

其次,EF Core目前不会将GroupBy查询转换为SQL,但会在内存中处理它们(所谓的client evaluation),这会使它们效率极低。考虑到这一点,您最好将工作分为两个查询 - 一个用于获取第一组的ClientName,第二个用于获取所需的结果:

var baseQuery = _DBContext.BatchRequests
    .Where(x => x.BatchId == null)
    .OrderBy(x => x.CreatedDateTime);

var clientName = await baseQuery
    .Select(x => x.ClientName)
    .FirstOrDefaultAsync();

var result = await baseQuery
    .Where(x => x.ClientName == clientName)
    .Take(5)
    .ToListAsync();

Actualy你可以将两个查询结合起来,但我不确定它是否会更有效(可能更糟):

var baseQuery = _DBContext.BatchRequests
    .Where(x => x.BatchId == null)
    .OrderBy(x => x.CreatedDateTime);

var result = await baseQuery
    .Where(x => x.ClientName == baseQuery.Select(y => y.ClientName).FirstOrDefault())
    .Take(5)
    .ToListAsync();

答案 1 :(得分:2)

您必须将组结果投影为:

result = await _DBContext.BatchRequests
            .Where(x => x.BatchID.HasValue == false)
            .OrderBy(x => x.CreatedDateTime)
            .ThenBy(x => x.ClientName)
            .GroupBy(x => x.ClientName)
            .Select( x => new { ClientName= x.ClientName,
                                 TopFive = x.Take(5)
                         })
            .FirstAsync();