2015-02-11 58 views
4

我有一個來自在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

回答

1

我真的懷疑你真的想在代碼中的任何一點使用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 <12完全不同。謝謝。 – WPalombini 2015-02-11 22:39:23

2

嘗試是這樣的分組之前把它在內存:

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

0

以快看我會調查你的部分grp.FirstOrDefault - 這真的是你想要做什麼?

-1

我肯定會在這種情況下去參數化存儲過程。你也應該考慮在你需要的表上創建一個覆蓋索引。這些步驟通常會以非常顯着的數量提升性能。