我有以下代码,这是错误的:
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
:
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
相同的效果,但现在所有繁重的工作都由数据库而不是在内存中完成。
关于c# - 如何修复执行多个 SQL 语句的超慢 EF/LINQ 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11125511/