简体   繁体   English

LINQ查询性能低下

[英]Slow performance in LINQ query

I have this t-sql query that is from an old system that run on Cold Fusion. 我有这个来自在Cold Fusion上运行的旧系统的t-sql查询。 This query takes less than one second to return the records. 此查询返回记录所需的时间不到一秒。

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'})

I am now converting this to the new system using .NET and LINQ. 我现在使用.NET和LINQ将其转换为新系统。 I managed to write this LINQ query which gives me the same results. 我设法写了这个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()
});

However, this query takes no less than 5 seconds to run, while with the first one (t-sql) it takes less than 1 second. 但是,此查询运行时间不少于5秒,而第一个查询(t-sql)则需要不到1秒。

By using Glimpse, we could see the t-sql that that LINQ query generates. 通过使用Glimpse,我们可以看到LINQ查询生成的t-sql。 It has multiple sub selects and it is 5 times longer than the fast query. 它有多个子选择,比快速查询长5倍。

How could I improve the LINQ query? 我怎样才能改进LINQ查询?

Try something like this to bring it in to memory before the grouping: 尝试这样的东西,在分组之前将它带入内存:

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

I really doubt you actually want to use FirstOrDefault() at any point in your code. 我真的怀疑你真的想在你的代码中的任何一点使用FirstOrDefault()

BTW It looks like you are using LinqToSQL as your Linq provider. BTW看起来您正在使用LinqToSQL作为您的Linq提供商。 That thing is nasty, inefficient and downright buggy. 那东西是讨厌,低效和彻头彻尾的马车。 You should switch to EntityFramework if its at all possible 如果可能的话,你应该切换到EntityFramework

Given that...perhaps you should try this... 鉴于......也许你应该尝试这个......

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

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

I would definitely go for a parametrized Stored Procedure in this case. 在这种情况下,我肯定会选择参数化的存储过程。 You should also consider creating a covering index on the tables you need. 您还应该考虑在所需的表上创建覆盖索引。 These steps usually boost performance by a very noticable amount. 这些步骤通常会以非常显着的数量提升性能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM