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

标签 c# .net 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);

第一行,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/

相关文章:

.net - 在 WIX Bootstrapper 中检查 .NET 4.0 框架时出现问题

.net - 具有两个不同客户端的两种绑定(bind)类型的 WCF 服务

c# - 当列为字符串时动态排序 ObservableCollection<object>

php - SQL : Select all Posts from Followers

c# - Windows Phone 7.1 应用程序中的自动登录过程

c# - 系统.UnauthorizedAccessException : Retrieving the COM class factory for Word Interop fails with error 80070005

c# - 如何在负载平衡环境中获取 DNS 名称?

sql - 用于收集和查询每个日期的文本统计信息的工具

C# WinForms 应用程序 : stop/interrupting a Windows Forms application

c# - 无法将 lambda 表达式转换为类型 'int'