实体框架:按月份有效分组

本文关键字:有效 框架 实体 | 更新日期: 2023-09-27 18:29:10

我对此做了一些研究,到目前为止,我发现最好的方法是在整个数据集上使用Asenumerable,这样就可以在linq中对对象而不是数据库进行过滤。我用的是最新的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字段来进行本机分组。然而,更改DB并不是一个容易的解决方案,因此任何建议都将不胜感激。

线程我发现了上面的代码想法(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支持的所有DB提供程序。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]