5

我有以下代码,这是不正常的:

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 秒内运行。通常,我可以JOIN使用该Include方法指定。但是,这似乎不适用于属性。换句话说,我不能这样做:

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

有没有办法优化这个 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);

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

4

1 回答 1

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但现在所有繁重的工作都由数据库完成,而不是在内存中完成。

于 2012-06-20T18:15:19.253 回答