繁体   English   中英

Linq / Entity Framework select 最新为一组记录

[英]Linq / Entity Framework select latest recorded for a group

我想弄清楚如何编写 LINQ / Entity Framework 查询以返回表中每个符号可用的最新数据。

我的数据库表如下所示:

ID    symbol    price_date    price
------------------------------------
1     AAPL      2022-02-28    174.50
2     MSFT      2022-02-28    307.20
3     AAPL      2021-03-01    172.23
4     MSFT      2021-03-01    304.15

但是,并非每个符号都有每天的记录。 ID 密钥是连续的,可以安全使用,因为给定符号的最高 ID 将包含最新数据。

如果我正在编写一个 SQL 查询,以下将返回我正在寻找的内容:

select prices.*
from prices 
where id in (select max(id) from prices group by symbol)

在 Linq 中,我无法将其纳入单个查询。 到目前为止,我将其分为两个查询:

var maxIds = from pp in ctx.Prices
             group pp by pp.Symbol
                 into maxIdBySymbol
             select maxIdBySymbol.Max(pp => pp.Id);

var latestPrices = ctx.Prices.Where(it => maxIds.Contains(it.Id)).ToList();

有没有办法使它成为 LINQ 中的单个查询?

谢谢

另外:建议的解决方案有效,但效率较低
更多信息见文末补充。

原液

因此,您将首先创建记录组,其中每个组仅包含一个特定交易品种的记录。 因此,您将有一组包含代码 AAPL 的记录,另一组包含代码 MSFT 的记录,等等。

我正在尝试...查询...表中每个交易品种的最新可用数据。

所以,一旦你得到了组,你 select 组中的一个元素。 根据您的要求,您 select 是最新元素,这是PriceDate值最高的元素。 如您所说,您还可以采用属性ID值最高的元素。 就我个人而言,我不会这样做,因为如果在很远的将来你的 ID 不再处于升序日期,例如因为你添加了在输入错误后编辑 PriceDate 的功能。

为此,我将使用具有参数 resultSelector 的 Queryable.GroupBy 的重载 使用 resultSelector 到 select 您想要的每个组中的一个元素。

var newestRecordPerSymbol = dbContext.PriceRecords

// make groups of priceRecords with same value for property Symbol
.GroupBy( priceRecord => priceRecord.Symbol,

// parameter resultSelector: for every symbol and all priceRecords
// that have this symbol, take the newest one
// = order by descending PriceDate and take the first one
(symbol, priceRecordsWithThisSymbol) => priceRecordsWithThisSymbol
    .OrderByDescending(priceRecord => priceRecord.PriceDate)
    .FirstOrDefault();

换句话说:从 PriceRecords 表中,创建具有相同属性 Symbol 值的 PriceRecords 组。 从 Symbol 和具有此符号的 PriceRecords 的每个组合中,按属性 PriceDate 的降序值对所有 PriceRecords 进行排序,并仅保留第一个。

每个组至少有一个元素,因此您可以使用FirstFirstOrDefault 某些版本的 EntityFramework 或 DBMS 在使用 First 时会出现问题。 如果遇到此问题,请使用 FirstOrDefault。

如果您仍想使用 ID 最高的那个:

  .OrderByDescending(priceRecord => priceRecord.ID)
  .FirstOrDefault(),

为什么这个解决方案效率较低。

原方案中,对一组中的所有记录进行排序,只取第一条。 如果只取第一个元素,则对第二个、第三个等元素进行排序有点浪费。

在原始 SQL 中,您会看到如下代码:

select maxIdBySymbol.Max(pp => pp.Id);

因此,并非所有元素都已排序。 该序列只枚举一次,返回最大的一个。 这比对您不会使用的元素进行排序更有效。

要创建这样的代码,我们需要更改 GroupBy 的参数 resultSelector。 让我们使用像Max(propertySelector)这样的方法,或者Queryable.Aggregate的重载之一。 是这样的:

// parameter resultSelector: keep the record with the largest ID
(symbol, priceRecordsWithThisSymbol) => priceRecordsWithThisSymbol
    .Max(record => record.Id);

唉,尽管实体框架的人做了很多工作,但不支持 Max 方法的这种重载,因此也不支持聚合方法。 请参阅支持和不支持的 Linq 方法列表

您可以将Wherenot Any结合使用:

ctx.Prices.Where(prices1 => !ctx.Prices.Any(prices2 => (prices2.Id > prices1.Id) && (prices1.symbol.Equals(prices2.symbol))))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM