Entity Framework multiple aggregation performance

痞子三分冷 提交于 2019-12-06 12:29:26
Ivan Stoev

I've noticed that behavior (bug?) when answering How do I get EF6 to generate efficient SQL containing mulitple aggregate columns?.

The only way I was able to resolve it was by introducing a temporary projection before the group by operation, and the same applies to your case:

var query =
    from e in (from d in db.StringDatas.AsNoTracking()
               where d.DCString.DCDistributionBox.DataLogger.ProjectID == projectID
                   && d.TimeStamp >= fromDate && d.TimeStamp < tillDate
               select new { d, s = d.DCString })
    group e by DbFunctions.AddMinutes(DateTime.MinValue, DbFunctions.DiffMinutes(DateTime.MinValue, e.d.TimeStamp) / minuteInterval * minuteInterval) into g
    let ratio = g.Select(e => e.d.DCCurrent / e.s.CurrentMPP)
    select new
    {
        TimeStamp = g.Key,
        DCCurrentMin = ratio.Min(),
        DCCurrentMax = ratio.Max(),
        DCCurrentAvg = ratio.Average(),
        DCCurrentStDev = DbFunctions.StandardDeviation(ratio)
    };

EF generated SQL:

SELECT 
    1 AS [C1], 
    [GroupBy1].[K1] AS [C2], 
    [GroupBy1].[A1] AS [C3], 
    [GroupBy1].[A2] AS [C4], 
    [GroupBy1].[A3] AS [C5], 
    [GroupBy1].[A4] AS [C6]
    FROM ( SELECT 
        [Project1].[K1] AS [K1], 
        MIN([Project1].[A1]) AS [A1], 
        MAX([Project1].[A2]) AS [A2], 
        AVG([Project1].[A3]) AS [A3], 
        STDEV([Project1].[A4]) AS [A4]
        FROM ( SELECT 
            DATEADD (minute, ((DATEDIFF (minute, @p__linq__4, [Project1].[TimeStamp])) / @p__linq__5) * @p__linq__6, @p__linq__3) AS [K1], 
            [Project1].[DCCurrent] / [Project1].[CurrentMPP] AS [A1], 
            [Project1].[DCCurrent] / [Project1].[CurrentMPP] AS [A2], 
            [Project1].[DCCurrent] / [Project1].[CurrentMPP] AS [A3], 
            [Project1].[DCCurrent] / [Project1].[CurrentMPP] AS [A4]
            FROM ( SELECT 
                [Extent1].[TimeStamp] AS [TimeStamp], 
                [Extent1].[DCCurrent] AS [DCCurrent], 
                [Extent2].[CurrentMPP] AS [CurrentMPP]
                FROM    [dbo].[StringDatas] AS [Extent1]
                INNER JOIN [dbo].[DCStrings] AS [Extent2] ON [Extent1].[DCStringID] = [Extent2].[ID]
                INNER JOIN [dbo].[DCDistributionBoxes] AS [Extent3] ON [Extent2].[DCDistributionBoxID] = [Extent3].[ID]
                INNER JOIN [dbo].[DataLoggers] AS [Extent4] ON [Extent3].[DataLoggerID] = [Extent4].[ID]
                WHERE ([Extent4].[ProjectID] = @p__linq__0) AND ([Extent1].[TimeStamp] >= @p__linq__1) AND ([Extent1].[TimeStamp] < @p__linq__2)
            )  AS [Project1]
        )  AS [Project1]
        GROUP BY [K1]
    )  AS [GroupBy1]

Try grouping the two properties you need rather than the whole object like this:

group new { d.DCCurrent, d.DCString.CurrentMPP } 

Then change v.DCString.CurrentMPP to v.CurrentMPP


Try setting the UseDatabaseNullSemantics to true.

This will eliminate all the is null checks and should allow the query optimizer to produce a similar execution plan to your original sql.

using(var model = new YourContext())
{
   model.Configuration.UseDatabaseNullSemantics = true;
   var query = from d in model.StringDatas
                ...
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!