【问题标题】:Performance issue with Linq query - navigation properties to blame?Linq 查询的性能问题 - 应该归咎于导航属性?
【发布时间】:2015-08-06 00:22:32
【问题描述】:

我有一个 Linq 查询花费的时间比它的等效 SQL 长得多(Linq 大约 5-6 秒,SQL 大约 50 毫秒)。显然翻译出了点问题,我什至知道是什么,但我不知道如何解决。查询如下:

from data in DbContext.CoacheeData
group data by new { data.User.CompanyId, data.Date } into g
select new GroupedDayData
{
    Id = g.FirstOrDefault().User.CompanyId == null ? "none" : g.FirstOrDefault().User.CompanyId,
    Name = g.FirstOrDefault().User.Company.Name == null ? "none" : g.FirstOrDefault().User.Company.Name,
    Date = g.FirstOrDefault().Date,
    Steps = g.Sum(x => x.Steps),
    Distance = g.Sum(x => x.Distance),
    CaloriesBurned = g.Sum(x => x.CaloriesBurned),
    LightlyActiveMinutes = g.Sum(x => x.LightlyActiveMinutes),
    FairlyActiveMinutes = g.Sum(x => x.FairlyActiveMinutes),
    VeryActiveMinutes = g.Sum(x => x.VeryActiveMinutes),
};

我怀疑在我的 group by 中使用导航属性 User 会导致为 CoacheeData 中的每条记录执行子查询。通过手动加入用户和公司表,我可以将效率提高大约 1.5 秒,如下所示:

from data in DbContext.CoacheeData
join user in DbContext.Users on data.UserId equals user.Id
join company in DbContext.Companies on user.CompanyId equals company.Id
group data by new { user.CompanyId, company.Name, data.Date } into g
select new GroupedDayData
{
    Id = g.FirstOrDefault().User.CompanyId == null ? "none" : g.FirstOrDefault().User.CompanyId,
    Name = g.FirstOrDefault().User.Company.Name == null ? "none" : g.FirstOrDefault().User.Company.Name,
    Date = g.FirstOrDefault().Date,
    Steps = g.Sum(x => x.Steps),
    Distance = g.Sum(x => x.Distance),
    CaloriesBurned = g.Sum(x => x.CaloriesBurned),
    LightlyActiveMinutes = g.Sum(x => x.LightlyActiveMinutes),
    FairlyActiveMinutes = g.Sum(x => x.FairlyActiveMinutes),
    VeryActiveMinutes = g.Sum(x => x.VeryActiveMinutes),
};

但我的选择看起来仍然相同,也许那里的导航属性导致了类似的问题?不过这看起来很奇怪,因为我之前在选择 Linq 查询时使用了 navigatio 属性而没有问题。无论如何,我不确定我的怀疑是否正确,如果正确,为什么会发生以及如何解决。

【问题讨论】:

  • 不需要g.FirstOrDefault(),因为只有在groupby返回集合时才会运行选择。

标签: c# performance linq


【解决方案1】:

我怀疑获取 CompanyIdCompanyNameDate 的表达式过于复杂,Linq To Sql 无法在适当的 Sql 中进行翻译.

1) 尝试删除选择 CompanyIdCompanyNameDate 的部分,看看它是否变得更快;

var result =
    from data in DbContext.CoacheeData
    join user in DbContext.Users on data.UserId equals user.Id
    join company in DbContext.Companies on user.CompanyId equals company.Id
    group data by new { CompanyId = user.CompanyId, CompanyName = company.Name, Date = data.Date } into g
    select new GroupedDayData
    {
        Steps = g.Sum(x => x.Steps),
        Distance = g.Sum(x => x.Distance),
        CaloriesBurned = g.Sum(x => x.CaloriesBurned),
        LightlyActiveMinutes = g.Sum(x => x.LightlyActiveMinutes),
        FairlyActiveMinutes = g.Sum(x => x.FairlyActiveMinutes),
        VeryActiveMinutes = g.Sum(x => x.VeryActiveMinutes),
    };

2) 如果有帮助,请将您的表达式替换为 g.Key.CompanyIdg.Key.CompanyNameg.Key.Date 之类的内容

var result =
    from data in DbContext.CoacheeData
    join user in DbContext.Users on data.UserId equals user.Id
    join company in DbContext.Companies on user.CompanyId equals company.Id
    group data by new { CompanyId = user.CompanyId, CompanyName = company.Name, Date = data.Date } into g
    select new GroupedDayData
    {
        Id = g.Key.CompanyId,
        Name = g.Key.CompanyName,
        Date = g.Key.Date,
        Steps = g.Sum(x => x.Steps),
        Distance = g.Sum(x => x.Distance),
        CaloriesBurned = g.Sum(x => x.CaloriesBurned),
        LightlyActiveMinutes = g.Sum(x => x.LightlyActiveMinutes),
        FairlyActiveMinutes = g.Sum(x => x.FairlyActiveMinutes),
        VeryActiveMinutes = g.Sum(x => x.VeryActiveMinutes),
    };

3) 如果这有帮助,请添加空检查,您应该会得到相同的结果,但速度更快;

var result =
    from data in DbContext.CoacheeData
    join user in DbContext.Users on data.UserId equals user.Id
    join company in DbContext.Companies on user.CompanyId equals company.Id
    group data by new { CompanyId = user.CompanyId, CompanyName = company.Name, Date = data.Date } into g
    select new GroupedDayData
    {
        Id = g.Key.CompanyId ?? "None",
        Name = g.Key.CompanyName ?? "None",
        Date = g.Key.Date,
        Steps = g.Sum(x => x.Steps),
        Distance = g.Sum(x => x.Distance),
        CaloriesBurned = g.Sum(x => x.CaloriesBurned),
        LightlyActiveMinutes = g.Sum(x => x.LightlyActiveMinutes),
        FairlyActiveMinutes = g.Sum(x => x.FairlyActiveMinutes),
        VeryActiveMinutes = g.Sum(x => x.VeryActiveMinutes),
    };

【讨论】:

  • 感谢您的提示。删除 CompanyId 和 CompanyName 确实大大提高了性能(将其降低到大约 250 毫秒,这很好)。但是,用 Key 替换 FirstOrDefault() 调用并没有改变性能(仍然大约 4 秒),null 检查也没有。
  • 能否请您发布 LINQ 生成的 SQL 查询?您可以使用 SQL Server Profiler 来提取查询。
  • 另外,尝试在不使用别名GroupBy 键的情况下执行查询。 ... 按新的 {user.CompanyId, company.Name, data.Date} 将数据分组到 g 中选择新的 GroupedDayData { Id = g.Key.CompanyId, Name = g.Key.Name, Date = g.Key.Date,步数 = g.Sum(x => x.Steps), ...
  • 啊,现在说得通了。当我阅读您最初的答案时,我确信使用 g.Key 而不是我的 g.FirstOrDefault() 将是解决方案,所以当它没有时我有点困惑。原来这是我的错:我在约会时仍然有一个FirstOrDefault,但我忘记换掉了。当我这样做时,性能再次提高到 200 毫秒左右。简而言之,您的第一个答案是正确的,并且解决了我的问题。非常感谢!
  • 抱歉,我的答案文本中错过了日期字段。更新了我的答案。很高兴它有帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 2019-12-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多