EF Core实现按CounterOwner.Name过滤后分组计算CounterEntry每日数据差值的数据库端查询方案
EF Core实现按CounterOwner.Name过滤后分组计算CounterEntry每日数据差值的数据库端查询方案
我完全懂你的需求——不想把所有数据拉到内存里折腾,要让数据库来完成过滤、分组和差值计算,这确实是更高效的做法,用EF Core的LINQ查询就能实现,而且能直接转换成对应的SQL逻辑。
下面是具体的实现方案,分两种写法(取决于你用的EF Core版本):
方案一:兼容EF Core 5及以下版本
这种写法用OrderByDescending().First()和OrderBy().First()来获取每组的最后一条和第一条记录,EF Core会把它转换成SQL里的TOP 1查询,所有计算都在数据库端完成:
using Microsoft.EntityFrameworkCore; // 替换成你的上下文实例和目标名称 var targetOwnerName = "张三"; var dailyDifferences = await dbContext.CounterEntries // 关联CounterOwner并过滤名称(EF Core会自动生成JOIN语句,无需手动写SQL) .Where(ce => ce.CounterOwner.Name == targetOwnerName) // 按日期的“天”分组,把DateTime截断到日期部分 .GroupBy(ce => ce.DateTime.Date) // 投影成匿名对象,计算每日差值 .Select(g => new { Date = g.Key, // 当日Heating的差值:最后一条记录值 - 第一条记录值 HeatingDailyUsage = g.OrderByDescending(ce => ce.DateTime).First().Heating - g.OrderBy(ce => ce.DateTime).First().Heating, // 各个水表的每日差值同理 WaterCounter1DailyUsage = g.OrderByDescending(ce => ce.DateTime).First().WaterCounter1 - g.OrderBy(ce => ce.DateTime).First().WaterCounter1, WaterCounter2DailyUsage = g.OrderByDescending(ce => ce.DateTime).First().WaterCounter2 - g.OrderBy(ce => ce.DateTime).First().WaterCounter2, WaterCounter3DailyUsage = g.OrderByDescending(ce => ce.DateTime).First().WaterCounter3 - g.OrderBy(ce => ce.DateTime).First().WaterCounter3, WaterCounter4DailyUsage = g.OrderByDescending(ce => ce.DateTime).First().WaterCounter4 - g.OrderBy(ce => ce.DateTime).First().WaterCounter4 }) .ToListAsync();
方案二:EF Core 6+版本简化写法
EF Core 6新增了MaxBy和MinBy方法,能更简洁地获取每组中DateTime最大/最小的记录,代码可读性更好:
using Microsoft.EntityFrameworkCore; var targetOwnerName = "张三"; var dailyDifferences = await dbContext.CounterEntries .Where(ce => ce.CounterOwner.Name == targetOwnerName) .GroupBy(ce => ce.DateTime.Date) .Select(g => new { Date = g.Key, HeatingDailyUsage = g.MaxBy(ce => ce.DateTime).Heating - g.MinBy(ce => ce.DateTime).Heating, WaterCounter1DailyUsage = g.MaxBy(ce => ce.DateTime).WaterCounter1 - g.MinBy(ce => ce.DateTime).WaterCounter1, WaterCounter2DailyUsage = g.MaxBy(ce => ce.DateTime).WaterCounter2 - g.MinBy(ce => ce.DateTime).WaterCounter2, WaterCounter3DailyUsage = g.MaxBy(ce => ce.DateTime).WaterCounter3 - g.MinBy(ce => ce.DateTime).WaterCounter3, WaterCounter4DailyUsage = g.MaxBy(ce => ce.DateTime).WaterCounter4 - g.MinBy(ce => ce.DateTime).WaterCounter4 }) .ToListAsync();
关键说明
- 数据库端执行:这两种写法都会被EF Core转换成包含
GROUP BY、JOIN和聚合逻辑的SQL,所有计算都在数据库服务器完成,不会把大量CounterEntry数据加载到内存,性能更优。 - 日期分组逻辑:
ce.DateTime.Date会被EF Core转换成对应数据库的日期截断函数(比如SQL Server里的CAST([DateTime] AS DATE)),确保按天分组的正确性。 - 边界情况处理:如果某天只有一条记录,差值会自动为0,符合实际业务逻辑(当天没有数据变化)。
备注:内容来源于stack exchange,提问作者schgab
相关产品推荐
相关产品推荐

