Reputation: 981
I have this t-sql query that is from an old system that run on Cold Fusion. 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. I managed to write this LINQ query which gives me the same results.
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.
By using Glimpse, we could see the t-sql that that LINQ query generates. It has multiple sub selects and it is 5 times longer than the fast query.
How could I improve the LINQ query?
Upvotes: 4
Views: 2031
Reputation: 15772
I really doubt you actually want to use FirstOrDefault()
at any point in your code.
BTW It looks like you are using LinqToSQL as your Linq provider. That thing is nasty, inefficient and downright buggy. You should switch to EntityFramework if its at all possible
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(),
};
Upvotes: 1
Reputation: 11798
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.
Upvotes: -1
Reputation: 549
Taking a quick look I would investigate your section grp.FirstOrDefault - is this really what you want to do?
Upvotes: 0
Reputation: 117174
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()
});
Upvotes: 2