entity framework:按月进行有效分组

我已经对此做了一些研究,到目前为止我发现的最好的是在整个数据集上使用Asenumerable,以便过滤发生在对象而不是数据库中。 我正在使用最新的EF。

我的工作(但很慢)代码是:

var trendData = from d in ExpenseItemsViewableDirect.AsEnumerable() group d by new {Period = d.Er_Approved_Date.Year.ToString() + "-" + d.Er_Approved_Date.Month.ToString("00") } into g select new { Period = g.Key.Period, Total = g.Sum(x => x.Item_Amount), AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) }; 

这给了我几个月的YYYY-MM格式,以及总金额和平均金额。 但是每次都需要几分钟。

我的另一个解决方法是在SQL中执行更新查询,因此我有一个YYYYMM字段可以进行本机分组。 更改数据库不是一个简单的解决方案,因此任何建议将不胜感激。

我找到上述代码构思的线程(http://stackoverflow.com/questions/1059737/group-by-weeks-in-linq-to-entities)提到’等到.NET 4.0’。 最近有什么介绍有助于这种情况吗?

性能不佳的原因是整个表被提取到内存中(AsEnumerable())。 您可以像这样按年份和月份进行分组

 var trendData = (from d in ExpenseItemsViewableDirect group d by new { Year = d.Er_Approved_Date.Year, Month = d.Er_Approved_Date.Month } into g select new { Year = g.Key.Year, Month = g.Key.Month, Total = g.Sum(x => x.Item_Amount), AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) } ).AsEnumerable() .Select(g=>new { Period = g.Year + "-" + g.Month, Total = g.Total, AveragePerTrans = g.AveragePerTrans }); 

编辑

从我的响应中,原始查询试图在int和字符串之间进行连接,而EF不能将其转换为SQL语句。 我可以使用SqlFunctions类,但查询它变得有点难看。 所以我在分组之后添加了AsEnumerable (),这意味着EF将在服务器上执行组查询,将获得年,月等,但是自定义投影是在对象上进行的(在AsEnumerable ()之后是什么)。

说到逐月,我更喜欢以这种方式完成这项任务:

 var sqlMinDate = (DateTime) SqlDateTime.MinValue; var trendData = ExpenseItemsViewableDirect .GroupBy(x => SqlFunctions.DateAdd("month", SqlFunctions.DateDiff("month", sqlMinDate, x.Er_Approved_Date), sqlMinDate)) .Select(x => new { Period = g.Key // DateTime type }) 

因为它在分组结果中保留日期时间类型。

与cryss写的相似,我正在为EF做以下事情。 注意,我们必须使用EntityFunctions来调用EF支持的所有数据库提供程序。 SqlFunctions仅适用于SQLServer。

 var sqlMinDate = (DateTime) SqlDateTime.MinValue; (from x in ExpenseItemsViewableDirect let month = EntityFunctions.AddMonths(sqlMinDate, EntityFunctions.DiffMonths(sqlMinDate, x.Er_Approved_Date)) group d by month into g select new { Period = g.Key, Total = g.Sum(x => x.Item_Amount), AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) }).Dump(); 

尝试生成SQL(来自类似的模式):

 -- Region Parameters DECLARE @p__linq__0 DateTime2 = '1753-01-01 00:00:00.0000000' DECLARE @p__linq__1 DateTime2 = '1753-01-01 00:00:00.0000000' -- EndRegion SELECT 1 AS [C1], [GroupBy1].[K1] AS [C2], [GroupBy1].[A1] AS [C3] FROM ( SELECT [Project1].[C1] AS [K1], FROM ( SELECT DATEADD (month, DATEDIFF (month, @p__linq__1, [Extent1].[CreationDate]), @p__linq__0) AS [C1] FROM [YourTable] AS [Extent1] ) AS [Project1] GROUP BY [Project1].[C1] ) AS [GroupBy1]