【问题标题】:Slow performance in LINQ queryLINQ 查询性能缓慢
【发布时间】:2015-04-11 11:08:33
【问题描述】:

我有这个来自在 Cold Fusion 上运行的旧系统的 t-sql 查询。此查询不到一秒即可返回记录。

select  dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) p, count(*) c 
from    account 
where   createdAt <= {ts '2015-02-28 23:59:59'} 
and accountType = 'business' 
and dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) <12 
group by    dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'}) 
order by    dateDiff(month, dateAdd(hour, 11, createdAt), {ts '2015-02-28 23:59:59'})

我现在正在使用 .NET 和 LINQ 将其转换为新系统。 我设法编写了这个 LINQ 查询,它给了我相同的结果。

from a in db.Accounts
where SqlFunctions.DateDiff("Month", SqlFunctions.DateAdd("Hour", 11, a.createdAt), "2015-02-28 23:59:59") < 12
&& a.accountType == "business"
group a by SqlFunctions.DateDiff("Month", a.createdAt, "2015-02-28 23:59:59") into grp
orderby SqlFunctions.DateDiff("Month", grp.FirstOrDefault().createdAt, "2015-02-28 23:59:59")
select new ProgressViewModel.Data
{
     date = SqlFunctions.DateDiff("Month", grp.FirstOrDefault().createdAt, "2015-02-28 23:59:59"),
     amount = grp.Count()
});

但是,此查询的运行时间不少于 5 秒,而第一个查询 (t-sql) 的运行时间不到 1 秒。

通过使用 Glimpse,我们可以看到 LINQ 查询生成的 t-sql。它有多个子选择,比快速查询长 5 倍。

如何改进 LINQ 查询?

【问题讨论】:

  • 你有没有机会把上面的查询转成存储过程并返回结果并显示在网格中?如果是,那是更有效的方法
  • grp.FirstOrDefauilt() 看起来很可疑 - 你不是说grp.Key 吗?另外,为什么不是dateAdd 传递的参数,而不是数据库中的值?这应该允许您在createdAt 上使用索引(如果有的话)。
  • 能否告诉我们生成的 LINQ 查询是什么样的?
  • 我在您的 Linq 版本中找不到条件“createdAt

标签: c# .net sql-server linq


【解决方案1】:

在分组之前尝试这样的事情来记忆:

from ca in (
    from a in db.Accounts
    where SqlFunctions.DateDiff("Month", SqlFunctions.DateAdd("Hour", 11, a.createdAt), "2015-02-28 23:59:59") < 12 && a.accountType == "business"
    select a.createdAt).ToArray()
group a by new /* month diff */ into grp
orderby grp.Key
select new ProgressViewModel.Data
{
    date = grp.key,
    amount = grp.Count()
});

【讨论】:

  • 该死的......没有真正看就投了赞成票。刚刚意识到这是一个非常糟糕的查询,因为它是在内存中完成的
  • @Aron - 为什么记忆不好?很多时候,它比不引入它更快。这真的取决于你要优化什么。
  • 因此,在这种情况下,您至少可以将帐户转换为开销较低的帐户。
  • 您好 Enigmativity,我很欣赏您的回答,但是,我将使用在选择之前不将数据加载到内存中的那个。但我过去曾这样做过,因为首先将数据加载到内存中然后进行操作比从数据库中一次性加载所有数据要快。再次感谢。
【解决方案2】:

我真的怀疑您是否真的想在代码中的任何位置使用FirstOrDefault()

顺便说一句,您似乎正在使用 LinqToSQL 作为您的 Linq 提供程序。那东西是讨厌的,低效的和彻头彻尾的越野车。如果可能的话,你应该切换到 EntityFramework

鉴于...也许你应该试试这个...

var date = new Date(2015, 2, 28).AddDays(1);
var query = from account in context.Accounts
            where account.CreatedAt < date
            where account.accountType == "business"
            group account by 
                   SqlFunctions.DateDiff(
                            "Month", 
                             SqlFunctions.DateAdd(
                                   "Hour", 11, a.createdAt), 
                             date)
            into g
            where g.Key < 12
            order by g.Key ascending
            select new 
            {
                MonthsAgo = g.Key,
                Count = g.Count(),
            };

【讨论】:

  • 嗨,Aron,您的代码解决方案是最干净的,并且按预期工作。但是,没有从 linq 转换为 EF 这样的事情。它们是不同的东西,在我们的例子中,我们使用 EF 和 linq 来查询 db(通过 EF)。无论如何,感谢您的解决方案,选择 g.Key 并添加 where g.Key
【解决方案3】:

快速浏览一下,我会调查您的部分 grp.FirstOrDefault - 这真的是您想要做的吗?

【讨论】:

    【解决方案4】:

    在这种情况下,我肯定会选择参数化存储过程。您还应该考虑在您需要的表上创建一个覆盖索引。这些步骤通常会显着提高性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 2016-03-09
      • 1970-01-01
      • 2022-01-04
      • 2016-05-10
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多