如何用LINQ按半小时周期返回最大ReadingReplacementVersion记录
如何用LINQ获取每个半小时周期内ReadingReplacementVersion最大的记录?
我有一个数据集,包含这些字段:
IntervalPeriodTimestamp RegisterTypeCode ReadingReplacementVersion IntervalValue
每30分钟接收一次读数,但读数有时会被替换,ReadingReplacementVersion会随之递增。我需要用LINQ只返回每个半小时周期内ReadingReplacementVersion最大的记录列表。
我试过这段代码:
db.IntervalInformations .Where(ii => ii.ChannelInformation.RegisterTypeCode == "62") .OrderBy(ii => ii.IntervalPeriodTimestamp) .ThenBy(ii => ii.ChannelInformation.Meter.messageType342.ReadingReplacementVersionNumber) .ToList();
它返回了按时间戳和版本排序的列表,但我只想要每个周期里版本最大的那条记录。我已经用SQL实现了这个需求,代码如下:
WITH CTE AS ( select SerialNumber, RegisterTypeCode, IntervalStatusCode, UOM_Code, IntervalPeriodTimestamp, ReadingReplacementVersionNumber, IntervalValue, RN = ROW_NUMBER() OVER (PARTITION BY IntervalPeriodTimestamp ORDER BY ReadingReplacementVersionNumber desc) from MarketMessage as a inner join messageType342 as b on a.MarketMessageID = b.MarketMessageID inner join Meter as c on b.messageType342ID = c.MessageType342ID inner join ChannelInformation as d on c.MeterID = d.MeterID inner join IntervalInformation as e on d.ChannelInformationID = e.ChannelInformationID where MPRN = 101010101010 and CAST(IntervalPeriodTimestamp as date) between '1 feb 2018' and '28 feb 2018' and RegisterTypeCode = 62 ) select * from CTE WHERE RN = 1
不知道怎么把这段SQL转换成LINQ语句,求帮助!
我来帮你把这段SQL逻辑转换成对应的LINQ写法,有两种常用的思路可以实现你的需求:
方法1:分组后筛选每组最大版本记录
这种方式更直观,和你SQL的分组思路一致,先按IntervalPeriodTimestamp(半小时周期)分组,然后在每个分组里挑出ReadingReplacementVersionNumber最大的那条记录:
var result = db.IntervalInformations // 先加上SQL里的所有过滤条件 .Where(ii => ii.ChannelInformation.RegisterTypeCode == "62" && ii.ChannelInformation.Meter.messageType342.MarketMessage.MPRN == 101010101010 && ii.IntervalPeriodTimestamp.Date >= new DateTime(2018, 2, 1) && ii.IntervalPeriodTimestamp.Date <= new DateTime(2018, 2, 28)) // 按半小时周期分组 .GroupBy(ii => ii.IntervalPeriodTimestamp) // 每个分组内按版本号倒序,取第一条(也就是版本最大的) .Select(group => group.OrderByDescending(ii => ii.ChannelInformation.Meter.messageType342.ReadingReplacementVersionNumber) .FirstOrDefault()) // 过滤掉可能的空值(如果某个分组没有数据的话) .Where(item => item != null) .ToList();
方法2:模拟SQL的ROW_NUMBER()逻辑
如果你想更贴近SQL里CTE的写法,可以给每条记录计算一个“行号”,然后筛选行号为1的记录(也就是每个周期里版本最大的那条):
var query = db.IntervalInformations // 同样先加上所有过滤条件 .Where(ii => ii.ChannelInformation.RegisterTypeCode == "62" && ii.ChannelInformation.Meter.messageType342.MarketMessage.MPRN == 101010101010 && ii.IntervalPeriodTimestamp.Date >= new DateTime(2018, 2, 1) && ii.IntervalPeriodTimestamp.Date <= new DateTime(2018, 2, 28)) // 给每条记录计算行号:统计同周期内版本号比当前大的记录数+1,就是当前的行号 .Select(ii => new { IntervalInfo = ii, RowNumber = db.IntervalInformations .Where(other => other.IntervalPeriodTimestamp == ii.IntervalPeriodTimestamp && other.ChannelInformation.RegisterTypeCode == "62") .Count(other => other.ChannelInformation.Meter.messageType342.ReadingReplacementVersionNumber > ii.ChannelInformation.Meter.messageType342.ReadingReplacementVersionNumber) + 1 }) // 只保留行号为1的记录 .Where(x => x.RowNumber == 1) // 提取出原始的IntervalInformation对象 .Select(x => x.IntervalInfo) .ToList();
小提示:
- 方法1的性能通常更好,因为EF会把GroupBy转换成高效的SQL分组逻辑,尤其是在EF Core 3.0+版本中,GroupBy的支持非常完善;
- 确保你的EF导航属性(比如
ChannelInformation、Meter这些关联对象)配置正确,避免不必要的延迟加载导致性能问题; - 如果你的
IntervalPeriodTimestamp是精确到分钟的时间戳,分组时直接用它就可以对应每个半小时周期,和你的SQL逻辑一致。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

