2012-06-20 23 views
5

我有下面的代碼,這是行爲不端:如何解決超慢EF/LINQ查詢執行多個SQL語句

TPM_USER user = UserManager.GetUser(context, UserId); 
var tasks = (from t in user.TPM_TASK 
      where t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

第一行,UserManager.GetUser只是沒有在數據庫中的一個簡單的查詢,以獲得正確的TPM_USER記錄。但是,第二行會導致各種SQL混亂。

首先,它在這裏執行兩條SQL語句。第一個抓住在TPM_TASK每一行鏈接到該用戶,有時行數以萬計:

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
WHERE "Extent1".USERID = :EntityKeyValue1 

此查詢需要約18秒,有很多任務的用戶。我希望WHERE子句也包含STAGEID篩選器,這將刪除大部分行。

接下來,它似乎執行一個新的查詢每個TPM_PROJECTVERSION對上面列表中:

SELECT 
-- Columns 
FROM TPMDBO.TPM_PROJECTVERSION "Extent1" 
WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2) 

即使此查詢速度快,它如果用戶在一大堆有任務執行數百倍的項目。

我想生成的查詢看起來是這樣的:

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
INNER JOIN TPMDBO.TPM_PROJECTVERSION "Extent3" ON "Extent2".PROJECTID = "Extent3".PROJECTID AND "Extent2".VERSIONID = "Extent3".VERSIONID 
WHERE "Extent1".USERID = 5 and "Extent2".STAGEID > 0 and "Extent2".STAGEID <> 3 and "Extent3".STAGEID <= 10 

上面的查詢會在1秒左右運行。通常,我可以使用Include方法指定JOIN。但是,這似乎不適用於屬性。換句話說,我不能這樣做:

from t in user.TPM_TASK.Include("TPM_PROJECTVERSION") 

有什麼辦法來優化這個LINQ語句嗎?我使用.NET4和Oracle作爲後端數據庫。

解決方案:

該解決方案是基於以下柯克的建議,並努力因爲context.TPM_USERTASK不能直接查詢:

var tasks = (from t in context.TPM_TASK.Include("TPM_PROJECTVERSION") 
      where t.TPM_USER.Any(y => y.USERID == UserId) && 
      t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

確實結果嵌套SELECT而不是查詢TPM_USERTASK直接,但它似乎相當有效的無所謂。

回答

4

是的,您正在拉下特定用戶,然後引用關係TPM_TASK。它正在拉下與用戶有關的每項任務,正是它應該做的。當你這樣做時,沒有ORM SQL翻譯。你得到一個用戶,然後把他所有的任務存入內存,然後執行一些客戶端過濾。這一切都是使用延遲加載完成的,所以SQL將非常低效,因爲它無法對任何內容進行批量處理。

取而代之,重寫查詢,直接違背TPM_TASK和過濾器對用戶:

var tasks = (from t in context.TPM_TASK 
     where t.USERID == user.UserId && t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
     orderby t.DUEDATE, t.PROJECTID 
     select t); 

注意我們如何檢查t.USERID == user.UserId。這產生了與user.TPM_TASK相同的效果,但是現在所有的繁重工作都是由數據庫完成的,而不是在內存中完成的。

+0

不幸的是,這個想法是行不通的。 'TPM_TASK'沒有'USERID'屬性。用戶通過'TPM_USERTASK'表與任務相關,因爲它用於多對多關係,所以無法直接查詢。也許我最好在數據庫中創建一個視圖或什麼? –

+0

您當然也可以編寫查詢來針對'TPM_USERTASK'。我不確定你的模式是如何設置的,但是沿着'where t.TPM_TASK.Any(y => y.USERID == t.USER)'的行或者只是使用'join'來引入很多很多。你絕對不要爲了達到這個目的而搞亂觀點。 –

+0

好吧,搞砸了那個想法..我會在幾分鐘之內讓你知道.. –