【问题标题】:Using FirstOrDefault in a sub query在子查询中使用 FirstOrDefault
【发布时间】: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


【解决方案1】:

试试这个查询,它应该适用于任何 EF 版本。

var results = 
   from r in results
   from c in r.CaseCaseStatusses
      .OrderByDescending(cs => cs.Timestamp)
      .Take(1)
   where c != null && c.Status.IsClosed != true
   select r;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    • 2022-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多