2012-04-07 55 views
6

我正在研究一段由同事編寫的與我們公司使用的CRM應用程序接口的代碼。在這段代碼中有兩個LINQ to Entities查詢在我們的應用程序中被執行了很多次,並且我被要求優化它們,因爲其中一個非常慢。LINQ to Entities查詢需要很長時間才能編譯,SQL運行速度很快

這些都是疑問:

首先查詢,這個編譯幾乎瞬間。它得到相關信息從CRM數據庫,由應用程序給定關係ID的列表過濾:

from relation in context.ADRELATION 
where ((relationIds.Contains(relation.FIDADRELATION)) && (relation.FLDELETED != -1)) 
join addressTable in context.ADDRESS on relation.FIDADDRESS equals addressTable.FIDADDRESS 
    into temporaryAddressTable 
from address in temporaryAddressTable.DefaultIfEmpty() 
join mailAddressTable in context.ADDRESS on relation.FIDMAILADDRESS equals 
    mailAddressTable.FIDADDRESS into temporaryMailAddressTable 
from mailAddress in temporaryMailAddressTable.DefaultIfEmpty() 
select new { Relation = relation, Address = address, MailAddress = mailAddress }; 

第二個查詢,需時約4-5秒,以編譯,大約需要從人的信息數據庫(再次ID的列表過濾):

from role in context.ROLE 
join relationTable in context.ADRELATION on role.FIDADRELATION equals relationTable.FIDADRELATION into temporaryRelationTable 
from relation in temporaryRelationTable.DefaultIfEmpty() 
join personTable in context.PERSON on role.FIDPERS equals personTable.FIDPERS into temporaryPersonTable 
from person in temporaryPersonTable.DefaultIfEmpty() 
join nationalityTable in context.TBNATION on person.FIDTBNATION equals nationalityTable.FIDTBNATION into temporaryNationalities 
from nationality in temporaryNationalities.DefaultIfEmpty() 
join titelTable in context.TBTITLE on person.FIDTBTITLE equals titelTable.FIDTBTITLE into temporaryTitles 
from title in temporaryTitles.DefaultIfEmpty() 
join suffixTable in context.TBSUFFIX on person.FIDTBSUFFIX equals suffixTable.FIDTBSUFFIX into temporarySuffixes 
from suffix in temporarySuffixes.DefaultIfEmpty() 
where ((rolIds.Contains(role.FIDROLE)) && (relation.FLDELETED != -1)) 
select new { Role = role, Person = person, relation = relation, Nationality = nationality, Title = title.FTXTBTITLE, Suffix = suffix.FTXTBSUFFIX }; 

我已經設置了SQL事件探查器,並採取了從SQL查詢兩,然後運行它在SQL Server Management Studio中。兩個查詢都運行得非常快,即使有大量(〜1000)個ID。所以問題似乎在於編譯LINQ查詢。

我試圖使用編譯查詢,但由於那些只能包含原始參數,我不得不去除部分與篩選器,並應用Invoke()調用後,所以我不知道如果這幫助很大。此外,由於此代碼在WCF服務操作中運行,因此我不確定編譯後的查詢是否會在後續調用中仍然存在。

最後,我嘗試的是隻在第二個查詢中選擇一個列。雖然這顯然不能提供我需要的信息,但我認爲它會比我們現在選擇的200列更快。沒有這種情況下,它仍然需要4-5秒。我不是一個LINQ大師,所以我幾乎不能遵循這個代碼(我有一種感覺,它不是最佳編寫的,但不能把我的手指放在它上面)。任何人都可以給我提示,爲什麼會出現這個問題?

我剩下的唯一解決方案是手動選擇所有信息而不是加入所有這些表。然後我會以約5-6個查詢結束。不錯,我想,但是由於我沒有在這裏處理可怕的低效SQL(或者至少是可接受的無效率水平),所以我希望能夠避免這種情況。

在此先感謝,希望我能說清楚。如果沒有,請隨時詢問,我會提供更多詳細信息。


編輯: 我結束了我的實體框架添加協會(目標數據庫沒有指定的外鍵)正是如此重寫查詢:

context.ROLE.Where(role => rolIds.Contains(role.FIDROLE) && role.Relation.FLDELETED != -1) 
      .Select(role => new 
          { 
           ContactId = role.FIDROLE, 
           Person = role.Person, 
           Nationality = role.Person.Nationality.FTXTBNATION, 
           Title = role.Person.Title.FTXTBTITLE, 
           Suffix = role.Person.Suffix.FTXTBSUFFIX 
          }); 

似乎多了很多可讀性也更快。

感謝您的建議,我一定會記住爲不同數量的參數進行多個編譯查詢!

回答

1

Gabriels答案是正確的:使用已編譯的查詢。

看起來您正在爲每個WCF請求重新編譯它,這當然會破壞一次性初始化的目的。相反,將編譯後的查詢放入一個靜態字段中。

編輯:

做到這一點:發送最大負荷爲您服務,並暫停調試器10次。看看調用堆棧。它在L2S代碼或ADO.NET代碼中更頻繁地停止了嗎?這將告訴您問題是否仍然存在於L2S或SQL Server中。

接下來,我們來修復過濾器。我們需要將它推回到編譯後的查詢中。這是唯一可能通過將這樣的:

rolIds.Contains(role.FIDROLE) 

這樣:

role.FIDROLE == rolIds_0 || role.FIDROLE == rolIds_1 || ... 

您需要rolIds的每一個基數的新編譯的查詢。這很討厭,但有必要編譯它。在我的項目中,我已經完成了這項任務的自動化,但您可以在這裏做一次性解決方案。

我猜大多數查詢都只有很少的角色ID,所以你可以實現10個基數1-10的編譯查詢,如果基數超過10,你可以回退到客戶端過濾。

+0

正如我在加布裏埃爾通用汽車的回答中所說的那樣,我確實把它們放在了一個靜態的領域。我想,儘管如此,我仍然會嘗試110%。儘管如此,我將只能將部分放到編譯查詢中的where子句中嗎? – 2012-04-07 18:51:50

+0

我添加了更多的東西。 – usr 2012-04-07 19:13:03

+1

哦,關於基數事情的好主意!你是對的,大多數請求將少於10個角色id。絕對是要看的東西。謝謝! – 2012-04-07 19:27:34

0

如果您決定將查詢保留在代碼中,您可以編譯它。在運行應用程序時,您仍然需要編譯一次查詢,但隨後的所有調用都將使用已編譯的查詢。你可以看看這裏的MSDN幫助:http://msdn.microsoft.com/en-us/library/bb399335.aspx

另一種選擇是使用存儲過程並從代碼中調用過程。因此沒有編譯時間。

+0

我試過使用編譯查詢,但每次調用服務操作時仍需要4秒。我有一種感覺,它沒有被保留在請​​求之間(我確實是靜態的)。我也不能使用存儲過程,因爲如果我們觸摸數據庫而不是從中讀取數據庫,那麼使CRM包停止提供支持:/ 如果我重寫了查詢以使用導航屬性而不是加入? – 2012-04-07 18:04:55

相關問題