[英]How to fix super slow EF/LINQ query executing multiple SQL statements
我有以下代碼,這是行為不端:
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作為后端數據庫。
解:
此解決方案基於Kirk的以下建議,並且自context.TPM_USERTASK
起作用。無法直接查詢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
,但它似乎相當高效。
是的,您正在拉下特定用戶,然后引用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
相同的效果,但現在所有繁重的工作都是由數據庫而不是內存完成的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.