【问题标题】:How to group records and retrieve only first group with top N records如何对记录进行分组并仅检索具有前 N 条记录的第一组
【发布时间】:2018-07-27 19:30:27
【问题描述】:

我有以下记录集

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 个记录 组。

根据上面给定的记录集,它应该返回 ID 为 1、2、3、4、5 的记录,用于 ClientName B

这是我的查询

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

问题
1>查询返回ClientA
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]

【问题讨论】:

  • 您的OrderByGroupBy 之后没有多大意义 - 您必须订购组。那么你想如何订购这些组? (另外,一个简单的x.BatchID == null 可能会产生更好的 SQL。)
  • 仅供参考,EF Core 与 LINQ to SQL 或 EF6 没有任何共同之处。例如,正如您已经注意到的,它目前不会将 GroupBy 查询转换为 SQL。
  • 那么分组必须在 SQL 中完成。检索内存中的所有记录并在内存中进行分组并只取前N个有什么用。
  • 您将不得不等待 EF Core 2.1。这就是我认为 EF Core 尚未准备好投入生产的原因之一。

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


【解决方案1】:

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

其次,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();

实际上你可以结合这两个查询,但我不确定它是否会更有效(可能会更糟):

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

【讨论】:

    【解决方案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();
    

    【讨论】:

    • 没有用。它产生相同的结果并返回客户端B。事实上,SQL 探查器显示的 SQL 与我在上面的更新 1 中发布的查询完全相同
    • 你确定这是生成的 SQL 吗?
    • 是的,正如有人指出,当你有 group by 时 order by 没有影响
    猜你喜欢
    • 1970-01-01
    • 2013-11-23
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多