如何修复执行多个SQL语句的超慢EF/LINQ查询

Mik*_*sen 5 .net c# sql linq entity-framework

我有以下代码,这是行为不端:

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);
Run Code Online (Sandbox Code Playgroud)

第一行,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
Run Code Online (Sandbox Code Playgroud)

对于具有大量任务的用户,此查询大约需要18秒.我希望WHERE子句也包含STAGEID过滤器,这将删除大多数行.

接下来,它似乎为TPM_PROJECTVERSION上面列表中的每对执行一个新查询:

SELECT 
 -- Columns
 FROM TPMDBO.TPM_PROJECTVERSION "Extent1"
 WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2)
Run Code Online (Sandbox Code Playgroud)

即使这个查询很快,如果用户在一大堆项目中有任务,它也会执行几百次.

我想生成的查询看起来像:

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
Run Code Online (Sandbox Code Playgroud)

上面的查询将在大约1秒内运行.通常,我可以指定JOIN使用该Include方法.但是,这似乎不适用于属性.换句话说,我做不到:

from t in user.TPM_TASK.Include("TPM_PROJECTVERSION")
Run Code Online (Sandbox Code Playgroud)

有没有办法优化这个LINQ语句?我使用.NET4和Oracle作为后端数据库.

解:

此解决方案基于Kirk的以下建议,并且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);
Run Code Online (Sandbox Code Playgroud)

确实导致嵌套SELECT而不是TPM_USERTASK直接查询,但它似乎相当有效.

Kir*_*oll 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);
Run Code Online (Sandbox Code Playgroud)

请注意我们如何检查t.USERID == user.UserId。这产生了相同的效果,user.TPM_TASK但现在所有繁重的工作都是由数据库而不是在内存中完成的。