[英]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 进行排序,并仅保留第一个。
每个组至少有一个元素,因此您可以使用First
和FirstOrDefault
。 某些版本的 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 方法列表
您可以将Where
与not 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.