Linq/Entity Frameworkselect一组最新记录
Linq / Entity Framework select latest recorded for a group
我正在尝试弄清楚如何编写 LINQ/Entity Framework 查询以 return table.
中每个交易品种的最新可用数据
我的数据库 table 如下所示:
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 查询,下面的内容将 return 我正在寻找的内容:
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 中将其设为单个查询?
谢谢
您可以将 Where
与 结合使用 Any
:
ctx.Prices.Where(prices1 => !ctx.Prices.Any(prices2 => (prices2.Id > prices1.Id) && (prices1.symbol.Equals(prices2.symbol))))
补充:建议的解决方案可行,但效率较低
更多信息见文末补充。
原解
所以您首先要创建记录组,其中每组仅包含一个特定交易品种的记录。因此,您将有一组包含代码 AAPL 的记录,一组包含代码 MSFT 的记录,等等。
I am trying ... query ... the latest data available for each symbol in a table.
所以,一旦你有了组,你就 select 成为组中的一个元素。根据您的要求,您 select 最新的元素,即 PriceDate
具有最高值的元素。如您所说,您还可以取 属性 ID
中具有最高值的元素。就我个人而言,我不会这样做,因为如果在很远的将来你的 ID 不再处于升序日期,例如因为你添加了在输入错误后编辑 PriceDate 的功能。
为此,我会使用 overload of Queryable.GroupBy that has a parameter resultSelector。使用 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 的 table 中,创建具有相同值的 PriceRecords 组 属性 Symbol。从 Symbol 和具有此 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), or one of the overloads of Queryable.Aggregate 这样的方法。像这样:
// parameter resultSelector: keep the record with the largest ID
(symbol, priceRecordsWithThisSymbol) => priceRecordsWithThisSymbol
.Max(record => record.Id);
唉,虽然 entity framework 的人做了大量工作,但不支持 Max 方法的这种重载,聚合方法的 none 也不支持。参见 List of Supported and Unsupported Linq methods
我正在尝试弄清楚如何编写 LINQ/Entity Framework 查询以 return table.
中每个交易品种的最新可用数据我的数据库 table 如下所示:
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 查询,下面的内容将 return 我正在寻找的内容:
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 中将其设为单个查询?
谢谢
您可以将 Where
与 结合使用 Any
:
ctx.Prices.Where(prices1 => !ctx.Prices.Any(prices2 => (prices2.Id > prices1.Id) && (prices1.symbol.Equals(prices2.symbol))))
补充:建议的解决方案可行,但效率较低
更多信息见文末补充。
原解
所以您首先要创建记录组,其中每组仅包含一个特定交易品种的记录。因此,您将有一组包含代码 AAPL 的记录,一组包含代码 MSFT 的记录,等等。
I am trying ... query ... the latest data available for each symbol in a table.
所以,一旦你有了组,你就 select 成为组中的一个元素。根据您的要求,您 select 最新的元素,即 PriceDate
具有最高值的元素。如您所说,您还可以取 属性 ID
中具有最高值的元素。就我个人而言,我不会这样做,因为如果在很远的将来你的 ID 不再处于升序日期,例如因为你添加了在输入错误后编辑 PriceDate 的功能。
为此,我会使用 overload of Queryable.GroupBy that has a parameter resultSelector。使用 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 的 table 中,创建具有相同值的 PriceRecords 组 属性 Symbol。从 Symbol 和具有此 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), or one of the overloads of Queryable.Aggregate 这样的方法。像这样:
// parameter resultSelector: keep the record with the largest ID
(symbol, priceRecordsWithThisSymbol) => priceRecordsWithThisSymbol
.Max(record => record.Id);
唉,虽然 entity framework 的人做了大量工作,但不支持 Max 方法的这种重载,聚合方法的 none 也不支持。参见 List of Supported and Unsupported Linq methods