【发布时间】:2021-08-08 16:47:38
【问题描述】:
我们的一些 LINQ-to-SQL 查询存在重大性能问题。
我们有一个 Case 表,在 CaseStatus 上有一对多的关系(关系表是 CaseCaseStatus) 虽然,我们想要跟踪一个案例的所有状态,但只有最后一个真正有价值。
我们正在构建如下查询:
IQueryable<Case> results = Repository.GetFullList(); // Returns a Queryable
if(filter.OnlyOpen){
results = results
.Where(c => c.CaseCaseStatusses.Any()
&& c.CaseCaseStatusses.OrderByDescending(cs => cs.Timestamp).FirstOrDefault().Status.IsClosed != true);
}
results.Select(x => Dto(x)).ToList();
基本上我们想知道最后一个状态是否有一个属性“IsClosed == true”。
当我通过 SQL 分析器监视我的数据库时,我可以看到当我们点击 Select 语句时正在查询数据库,而不是之前。 (按原意) 但是在执行查询时,我可以看到每个案例的选择查询:
SELECT [cs0].[Id], [cs0].[CaseId], [cs0].[Note], [cs0].[ShowOnClientPage], [cs0].[StatusId], [cs0].[TimestampUTC], [cs.Status0].[IsClosed]
FROM [CaseCaseStatusses] AS [cs0]
INNER JOIN [CaseStatusses] AS [cs.Status0] ON [cs0].[StatusId] = [cs.Status0].[Id]
WHERE [cs0].[CaseId] = <CaseId>
由于我们的案例表中有 8000 多条记录,它执行 8000 条上述选择语句......而我们希望执行 1 个单个查询。 EFCore 中的这种行为是否发生了变化?我可以发誓我以前做过这个,它会输出一个查询(当然,这是一个不可读的大查询,而不是 EFCore)
还有其他(更好的)方法来执行这类查询吗?
【问题讨论】:
-
你首先想做什么?
FirstOrDefault in a sub query是你认为你会解决真正问题的方式,而不是问题本身。子查询中的SELECT TOP 1也不一定是基于第一个或最后一个值进行过滤的最有效方法。最后,同时调用CaseCaseStatusses.Any()和c.CaseCaseStatusses....是自相矛盾的。而IsClosed != true当结果可能是NULL时会导致更多问题 -
不幸的是,答案取决于 EF Core 版本。 1.x/2.x 使用客户端评估,并且在许多情况下会遇到上述 N + 1 查询问题。从 3.x/5.x 的另一端来看,它可能会简单地拒绝翻译并抛出异常。所以没有一般的建议。
-
LINQ-to-WhateverDatabase 既不是魔术,也不是 SQL 的替代品。它旨在使使用 ORM 进行查询更容易。反过来,ORM 旨在加载对象,而不是像您尝试执行的那样执行报告查询。在 SQL 中,您可以使用
FIRST_VALUE() OVER(...)或LAST_VALUE() OVER()获取结果集中的第一个或最后一个值。如果要查找最后状态未关闭的记录,可以使用LAST_VALUE(IsClosed) OVER(ORDER BY Timestamp desc) -
@PanagiotisKanavos 您多次分享此意见,我不同意。 LINQ 和 SQL 都是查询语言,LINQ-to-somedb is 应该是强类型的面向对象的 SQL 替代品。一些实现未能做到这一点的事实并没有改变这个想法。它还意味着与数据库无关,对于您作为示例给出的 SQL 特定函数,这不能说是。将描述“有序子集中的最后一项”的 LINQ 构造转换为相应的 db/SQL 构造是 LINQ 翻译器的工作。
-
@Shadowfox 您的目标是什么确切的 EF Core 版本,什么数据库和提供程序。您还可以提供重现(例如minimal reproducible example),因为使用 EFC 2.2.6 进行类似查询的简单测试将转换和执行为单个 SQL 查询(顺便说一句,Linq-to-Sql 是完全不同的框架。以及 EF6。不要t 将它们用作可互换的),
标签: entity-framework-core ef-core-2.2