LINQ查询中的性能下降

问题描述:

我有一个来自在Cold Fusion上运行的旧系统的t-sql查询。此查询只需不到一秒钟即可返回记录。LINQ查询中的性能下降

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查询?

+0

有没有可能给你把上面的查询到存储过程并返回结果和在网格中显示?如果是的话,这是更有效的方式 – Immu 2015-02-11 06:56:52

+4

'grp.FirstOrDefauilt()'看起来可疑 - 你不是指'grp.Key'?另外,为什么不'dateAdd'传递的参数,而不是数据库中的值?这应该允许你在'createdAt'上使用索引,如果有的话。 – Luaan 2015-02-11 07:02:34

+0

您能否让我们知道生成的LINQ查询的样子? – JunaidKirkire 2015-02-11 07:07:30

我真的怀疑你真的想在代码中的任何一点使用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(), 
      }; 
+0

嗨阿伦,你的代码解决方案是最干净的,它按预期工作。但是,从linq转换到EF没有这样的事情。它们是不同的东西,在我们的例子中,我们使用EF和linq来查询db(通过EF)。无论如何,感谢您的解决方案,选择g.Key并添加其中的g.Key WPalombini 2015-02-11 22:39:23

尝试是这样的分组之前把它在内存:

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() 
}); 
+0

该死的...... upvoted没有真正看。刚刚意识到这是一个非常糟糕的查询,因为它在内存中完成 – Aron 2015-02-11 07:58:03

+1

@Aron - 为什么在内存中很糟糕?通常情况下,它比不把它带入更快。这取决于你想要优化的东西。 – Enigmativity 2015-02-11 10:39:29

+0

因此,在这种情况下,您至少可以将帐户转换为开销较低的帐户。 – Aron 2015-02-11 14:23:31

以快看我会调查你的部分grp.FirstOrDefault - 这真的是你想要做什么?

我肯定会在这种情况下去参数化存储过程。你也应该考虑在你需要的表上创建一个覆盖索引。这些步骤通常会以非常显着的数量提升性能。