【问题标题】:How do I get the most recent entry by condition in EF Core?如何在 EF Core 中按条件获取最新条目?
【发布时间】:2021-11-16 13:57:52
【问题描述】:

我有一个具有以下结构(和示例数据)的表:

Identifier UseDate PartId
a123 05/01/2000 237
a123 05/01/2000 4656
a123 01/01/2000 2134
a124 04/01/2000 5234
a124 01/01/2000 2890

我需要获取每个(非唯一)标识符的最新条目,但每个标识符最多一个。

似乎可以解决我的问题的 SQL 查询(MariaDB)如下:

SELECT a.Identifier, a.MaxDate, b.PartId, b.UseDate 
FROM
(SELECT Identifier, MAX(UseDate) AS MaxDate FROM MyTable GROUP BY Identifier) a
LEFT JOIN MyTable b ON a.Identifier = b.Identifier
WHERE a.MaxDate = b.UseDate GROUP BY a.Identifier;

但是我需要它来使用 C# 和 EF Core (Pomelo.EntitiFrameworkCore.MySql 5.0.3),我的尝试是:

var q1 = db.MyTable
            .GroupBy(t => t.Identifier)
            .Select(t => new { Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) });

return new ObjectResult(db.MyTable
    .Join(
        q1,
        t1 => t1.Identifier,
        t2 => t2.Identifier,
        (t1, t2) => new { Identifier = t2.Identifier, PartId = t1.PartId, MaxDate = t1.MaxDate, UseDate = t1.UseDate })
    .Where(t => t.UseDate == q1.First(x => x.Identifier == t.Identifier).MaxDate)
    .GroupBy(t => t.Identifier)
    .ToList()
);

return new ObjectResult(db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => t.OrderByDescending(x => x.UseDate).FirstOrDefault())
    .ToList()
);

第一个抛出这个错误:

System.InvalidOperationException:“无法翻译给定的 'GroupBy' 模式。在 'GroupBy' 之前调用 'AsEnumerable' 以在客户端对其进行评估。”

第二个基本上产生相同的结果,只是抱怨 LINQ 表达式而不是 GroupBy。

我想避免使用原始 SQL,但如何正确(并希望有效地)实现它?

【问题讨论】:

  • 您的目标是哪个 EFC 版本?
  • 我正在使用 Pomelo.EntityFrameworkCore.MySql 5.0.3。

标签: c# sql entity-framework-core


【解决方案1】:

在 LINQ 中编写此类查询的方法有很多,其中大多数都可以通过 EF Core 5/6+ 进行翻译。

为必要的分组和聚合定义子查询后,直接的方法是将其加入数据表,但不能使用 join 运算符 - 而是使用行限制相关子查询(SelectManyWhereTake),例如

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => new { Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) })
    .SelectMany(g => db.MyTable
        .Where(t => t.Identifier == g.Identifier && t.UseDate == g.MaxDate)
        .Take(1));

如果排序字段对于每个其他键值都是唯一的(即在您的情况下,如果 UseDate 对于每个唯一的 Identifier 值都是唯一的),您可以直接使用 Join 运算符(因为需要限制),例如

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => new { Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) });
    .Join(db.MyTable,
        g => new { g.Identifier, UseDate = g.MaxDate },
        t => new { t.Identifier, t.UseDate },
        (g, t) => t);

或直接将Max基于Where条件应用于数据表:

var query = db.MyTable
    .Where(t => t.UseDate == db.MyTable
        .Where(t2 => t2.Identifier == t.Identifier)
        .Max(t2 => t2.UseDate)
    );

最后,获得每组前 1 项的“标准”LINQ 方式。

对于 EF Core 6.0+:

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(g => g
        .OrderByDescending(t => t.UseDate)
        .First());

对于 EF Core 5.0,必须模拟查询内的分组结果集:

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(g => db.MyTable
        .Where(t => t.Identifier == g.Key)
        .OrderByDescending(t => t.UseDate)
        .First());

【讨论】:

    猜你喜欢
    • 2015-12-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    相关资源
    最近更新 更多