为什么实体框架在 SELECT 上生成 JOIN

2024-04-18

我在 C# 应用程序中使用实体框架,并且使用延迟加载。我们注意到一个查询对我们的 CPU 有着极高的影响,它仅仅计算一个总和。调试实体框架生成的查询时,它会创建一个INNER JOIN (SELECT ...这不是高性能的。当我手动将查询更改为正确的 JOIN 时,查询时间从 1.3 秒变为 0.03 秒。

让我用我的代码的简化版本来说明它。

public decimal GetPortfolioValue(Guid portfolioId)
{
   var value = DbContext.Portfolios
        .Where( x => x.Id.Equals(portfolioId) )
        .SelectMany( p => p.Items
            .Where( i => i.Status == ItemStatusConstants.Subscribed 
                && _activeStatuses.Contains( i.Category.Status ) )
        )
        .Select( i => i.Amount )
        .DefaultIfEmpty(0)
        .Sum();

   return value;
}

这会生成一个查询,该查询选择总和,但对连接在一起的两个表的 SELECT 执行内部连接。我创建了一个pastebinhere https://pastebin.com/czeTSiqS为了生成的查询不会污染这个问题,但缩短的版本是:

SELECT ...
FROM `portfolios` AS `Extent1`
INNER JOIN (SELECT 
               `Extent2`.*,
               `Extent3`.*
            FROM `items` AS `Extent2`
            INNER JOIN `categories` AS `Extent3` ON `Extent3`.`id` = 
`Extent2`.`category_id`) AS `Join1`
ON `Extent1`.`id` = `Join1`.`portfolio_id`
    AND ((`Join1`.`status` = @gp1)
    AND (`Join1`.`STATUS1` IN (@gp2, @gp3, @gp4, @gp5, @gp6)))
WHERE ...

我期望它生成的查询(需要 0.03 秒而不是 1.3 秒)类似于

SELECT ...
FROM `portfolios` AS `Extent1`
INNER JOIN `items` AS `Extent2` ON `Extent2`.`portfolio_id` = `Extent1`.`id`
INNER JOIN `categories` AS `Extent3` ON `Extent3`.`id` = `Extent2`.`category_id`
    AND ((`Extent2`.`status` = @gp1)
    AND (`Extent3`.`status` IN (@gp2, @gp3, @gp4, @gp5, @gp6)))
WHERE ...

我怀疑这是由于.SelectMany但我不知道应该如何重写 LINQ 查询以使其更高效。对于实体,链接属性是虚拟的,并且配置了外键:

public class Portfolio
{
   public Guid Id { get; set; }
   public virtual ICollection<Item> Items { get; set; }
}

public class Item
{
   public Guid Id { get; set; }
   public Guid PortfolioId { get; set; }
   public Guid CategoryId { get; set; }
   public decimal Amount { get; set; }
   public string Status { get; set; }
   public virtual Portfolio Portfolio { get; set; }
   public virtual Category Category { get; set; }
}

public class Category
{
   public Guid Id { get; set; }
   public string Status { get; set; }
   public virtual ICollection<Item> Items { get; set; }
}

任何帮助将不胜感激!


由于您不需要 Portfolio 中的任何内容,只需按 PortfolioId 过滤即可直接查询 PortfolioItems。假设您的 DbContext 有一个包含所有投资组合中的所有项目的 DbSet,可能是这样的:

var value = DbContext.PortfolioItems
                     .Where(i => i.PortfolioId == portfolioId && i.Status == ItemStatusConstants.Subscribed && _activeStatuses.Contains(i.Category.Status))
                     .Sum(i=>i.Amount);                 

我相信如果您直接使用适当的 Queryable.Sum 重载,则不需要 DefaultIfEmpty 也不需要 select。

编辑:尝试了两个不同的 LINQ 查询,但没有公开 DbSet。

第一个查询与您的基本相同:

var value2 = dbContext.Portfolios
    .Where(p => p.Id == portfolioId)
    .SelectMany(p => p.Items)
    .Where(i => i.Status == "A" && _activeStatuses.Contains(i.Category.Status))
    .Select(i=>i.Amount)
    .DefaultIfEmpty()
    .Sum();

在 SQL Server 中分析查询(手头没有 MySql)并生成一个丑陋的句子(参数被替换并且引号未转义以进行测试):

SELECT [GroupBy1].[a1] AS [C1] 
FROM   (SELECT Sum([Join2].[a1_0]) AS [A1] 
    FROM   (SELECT CASE 
                     WHEN ( [Project1].[c1] IS NULL ) THEN Cast( 
                     0 AS DECIMAL(18)) 
                     ELSE [Project1].[amount] 
                   END AS [A1_0] 
            FROM   (SELECT 1 AS X) AS [SingleRowTable1] 
                   LEFT OUTER JOIN 
                   (SELECT [Extent1].[amount] AS [Amount], 
                           Cast(1 AS TINYINT) AS [C1] 
                    FROM   [dbo].[items] AS [Extent1] 
                           INNER JOIN [dbo].[categories] AS 
                                      [Extent2] 
                                   ON [Extent1].[categoryid] = 
                                      [Extent2].[id] 
                    WHERE  ( N'A' = [Extent1].[status] ) 
                           AND ( [Extent1].[portfolioid] = 
                                 'E2CC0CC2-066F-45C9-9D48-543D92C4C92E' ) 
                           AND ( [Extent2].[status] IN ( N'A', N'B', N'C' ) 
                               ) 
                           AND ( [Extent2].[status] IS NOT NULL )) AS 
                   [Project1] 
                                ON 1 = 1) AS [Join2]) AS [GroupBy1] 

如果我们删除“Select”和“DefaultIfEmpty”方法,并将查询重写为:

var value = dbContext.Portfolios
    .Where(p => p.Id == portfolioId)
    .SelectMany(p => p.Items)
    .Where(i => i.Status == "A" && _activeStatuses.Contains(i.Category.Status))
    .Sum(i => i.Amount);

生成的句子更加清晰:

SELECT [GroupBy1].[a1] AS [C1] 
FROM   (SELECT Sum([Extent1].[amount]) AS [A1] 
    FROM   [dbo].[items] AS [Extent1] 
           INNER JOIN [dbo].[categories] AS [Extent2] 
                   ON [Extent1].[categoryid] = [Extent2].[id] 
    WHERE  ( N'A' = [Extent1].[status] ) 
           AND ( [Extent1].[portfolioid] = 
                 'E2CC0CC2-066F-45C9-9D48-543D92C4C92E' ) 
           AND ( [Extent2].[status] IN ( N'A', N'B', N'C' ) ) 
           AND ( [Extent2].[status] IS NOT NULL )) AS [GroupBy1] 

结论:我们不能依赖 LINQ 提供程序来创建优化查询。在思考生成的 SQL 语句之前,必须对 linq 查询进行分析和优化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么实体框架在 SELECT 上生成 JOIN 的相关文章

  • 我应该在单元测试中使用 AutoMapper 吗?

    我正在为 ASP NET MVC 控制器方法编写单元测试 这些控制器依赖于IMapper 我创建的用于抽象 AutoMapper 的接口 使用 Castle Windsor 通过构造函数注入传入 动作方法使用IMapper从领域对象映射到
  • 浮点提升:stroustrup vs 编译器 - 谁是对的?

    在 Stroustrup 的新书 C 编程语言 第四版 第 10 5 1 节中 他说 在执行算术运算之前 整数提升用于从较短的整数类型创建整数 类似地 浮点提升是用于从浮点数创建双精度数 我用以下代码确认了第一个声明 include
  • MySQL InnoDB引擎是否对只读事务运行任何性能优化

    根据参考文档 只读事务标志可能会提示存储引擎运行一些优化 设置会话事务只读 如果事务访问模式设置为 READ ONLY 则对表进行更改 被禁止 这可能使存储引擎能够提高性能 不允许写入时可能进行的改进 InnoDB引擎是否对只读事务运行这样
  • Linq Where 本地计数器关闭在 VS watch 中的结果不同

    我尝试删除前 3 个元素array与 LinQWhere扩展功能 这是一个例子 var array new 1 2 3 4 5 6 7 8 9 var count 3 var deletedTest1 0 var test1 array W
  • 记录 Google Cloud SQL PostgreSQL 实例上的慢速查询

    我工作的公司使用 Google Cloud SQL 来管理生产中的 SQL 数据库 我们遇到了性能问题 我认为查看 监控高于特定阈值 例如 250 毫秒 的所有查询是一个好主意 除其他外 通过查看PostgreSQL 文档 https ww
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • C#:使用 System.Text 和 System.Text.RegularExpressions 之间的区别

    在 ASP NET C 应用程序中 我注意到为了使用 Regex 和 StringBuilder 我必须将两者都放在 using System Text using System Text RegularExpressions 从简单的角度
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i
  • 为什么以下代码不允许我使用 fgets 获取用户输入但可以使用 scanf?

    这是一个更大程序的简短摘录 但该程序的其余部分无关紧要 因为我认为我能够隔离该问题 我怀疑这与我使用 fgets 的方式有关 我读过 最好使用 fgets 而不是 scanf 但我似乎无法让它在这里正常工作 当我使用以下代码时 程序不会给我
  • 使用 Linq 进行异步Where过滤

    我有一个List通过填充的元素async调用 WebService 没问题 我需要过滤该列表以便在应用程序视图上显示某些内容 我试过这个 List
  • OpenMP C 程序运行速度比顺序代码慢

    我是 OpenMP 的新手 正在尝试并行化 Jarvis 的算法 然而事实证明 与顺序代码相比 并行程序花费的时间要长 2 3 倍 难道问题本身就不能并行化吗 或者我并行化它的方式有问题 这是我针对该问题的 openMP 程序 其中有 2
  • 如何使用Python3.4在tornado中进行异步mysql操作?

    我现在使用Python3 4 我想在Tornado中使用异步mysql客户端 我已经发现torndb https github com bdarnell torndb但在阅读其源代码后 我认为它无法进行异步mysql操作 因为它只是封装了M
  • 通过 MSBuild 调用 cl.exe 时无限期挂起

    我正在尝试在我的 主要是 C 项目上运行 MSBuild 想象一下一个非常庞大的代码库 Visual Studio 2015 是有问题的工具集 Windows 7 SP1 和 VS 2015 更新 2 即使使用 m 1 从而迫使它仅使用一个
  • 改进C++逐行读取文件的能力?

    我正在解析大约 500GB 的日志文件 我的 C 版本需要 3 5 分钟 我的 Go 版本需要 1 2 分钟 我正在使用 C 的流来流式传输文件的每一行以进行解析 include
  • 在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?

    在 MVVM 模式中 是否可以接受甚至可以访问视图代码后面的 ViewModel 属性 我有一个可观察的集合 它填充在 ViewModel 中 我需要在视图中使用它来绑定到带有链接列表的无限滚动条 IE private LinkedList
  • MySQL - 查找接近的匹配项

    MySQL 有没有办法在文本字段中找到紧密匹配的内容 说找到 email protected cdn cgi l email protection当搜索时 email protected cdn cgi l email protection
  • 有没有办法让 VS2010 在我的方法中扩展或收缩 try 块?

    我的代码有很多 try catch finally 块 与我在 VS2010 中的方法不同 除了添加区域之外 我无法在开发时扩展或收缩这些区域来隐藏内容 try vm R vm Qu vm T vm D vm Fil vm Type vm
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn
  • ASP.NET Core:会话 ID 始终变化

    今天启动了一个全新的 ASP NET Core 网站 按照说明添加会话 我们在索引页上打印出会话 ID 它始终是唯一的 我认为这可能是 cookie 合规性 所以我在 Chrome 的高级设置和调试器中删除了所有 cookie 但横幅不会再
  • 在 C# 中读取/写入命令行程序

    我正在尝试与 C 的命令行程序进行对话 它是一个情绪分析器 它的工作原理如下 CMD gt java jar analyser jar gt Starting analyser 这是我想从我的 C 程序插入内容的地方 例如 I love y

随机推荐

  • 从雅虎财经提取数据时出错

    我正在尝试通过 Pandas 从雅虎财经提取数据 我以前使用过类似的拉力 但在此之前没有遇到任何问题 import pandas as pd import numpy as np import datetime as dt from dat
  • Postgis - ST_within 没有做我想做的事。如何在空心区域中找到一个点?

    请参阅丝网印刷 我在 Postgis 中运行了一个空间查询 以返回地图上某个点所在的选区 区域 该查询使用ST within函数 其中点位于多边形内 正如您从打印中看到的 该点实际上并不在 York Outer 的多边形区域 内 尽管从技术
  • php中如何将多维关联数组转换为一维数组?

    我有一个快速查询如何将多维数组转换为一维数组 teachers array array post id gt John Doe video id gt array Government English array post id gt St
  • 在Android studio中添加外部库

    我想添加外部库https github com foursquare foursquare android oauth https github com foursquare foursquare android oauth到我的 Andr
  • 更改my.ini后MySQL服务无法启动

    我在 Windows 上运行 MySQL 8 0 我对 my ini 做了一些更改 最终找到了它 以更改字符集 愚蠢的是我没有先备份它 并且我使用了记事本 它没有多重撤消功能 现在我明白了 The MySQL Service could n
  • Kivy 中的 HTTPS 请求

    我一直在处理与通过 AWS API Gateway 托管的 API 绑定的 Kivy 应用程序中的 HTTPS 请求 首先 我从Python3迁移到2 然后从requests库迁移到kivy的URLRequest 该应用程序在我的 Linu
  • prawnto 显示新页面时不会中断的表格

    我有数量可变的表 行数可变 我想让它们一个接一个地显示 但如果当前页面不适合表 请将其放在下一页上 然后继续 我已将表格放入事务中 这样如果高度适合当前页面 我可以回滚然后打印它 但如何获取表格高度 我现在有这个代码 pdf transac
  • 将 CSV 导入组织模式属性

    我想将 CSV 导入组织模式 其他人已经询问过如何将 CSV 导入组织模式表 这不是我想做的 我需要将 CSV 导入到组织模式属性 例如 像这样的 CSV Name Tel Mobile Fax John 11111 22222 33333
  • 删除具有重复索引的 pandas 行

    如何删除具有重复索引值的行 在下面的天气数据框中 有时科学家会返回并纠正观察结果 不是通过编辑错误的行 而是通过将重复的行附加到文件末尾 我正在从网络上读取一些自动天气数据 每 5 分钟进行一次观测 并编译成每个气象站的每月文件 解析文件后
  • 使用 ffmpeg 将文件从一种格式转换为另一种格式

    我是新来的ffmpeg我试图找出如何将音频或视频文件从一种格式转换为另一种格式 我不想使用CLI 我只是想知道我是否可以使用ffmpeg作为库并调用函数将文件从一种格式转换为另一种格式 我浏览了文档并找到了函数avcodec encode
  • 如何仅在夹具级别执行“beforeEach”,而不是针对该夹具下的每个测试

    我只想在固定装置级别运行 beforeEach 而不是在该固定装置下的每个测试中运行 fixture Fixture A for Use Case1 beforeEach login test A Test 1 async t gt awa
  • 这是 Matlab 的错误吗?你有同样的问题吗? [复制]

    这个问题在这里已经有答案了 我的Matlab版本是R2012a为什么在Matlab中1 1 0 2不等于0 9 这太糟糕了 gt gt 1 1 0 2 0 9 ans 0 这不是Matlab问题 这是一个浮点问题 在 C 或任何符合以下标准
  • 使用 Google Drive API 从 Google Drive 直接下载

    我的桌面应用程序是用 java 编写的 尝试从 Google Drive 下载公共文件 据我发现 它可以通过使用文件来实现webContentLink 这是为了能够在未经用户授权的情况下下载公共文件 因此 下面的代码适用于小文件 Strin
  • 如何将两个或多个不同 csv 文件组成的数据框中的两列合并为一个新列?

    我有几个 csv 文件 全部以日期命名 对于所有文件 我想在每个文件中创建一个新列 其中包含来自其他两列放在一起的数据 然后 我想将它们组合成一个大数据框 并仅选择其中两列来保留 这是一个例子 假设我有两个数据框 a b c a b c x
  • Rails 的 javascript_include_tag 可以忽略之前加载的脚本吗?

    我正在使用这条线 javascript include tag all recursive gt true cache gt true 在 Rails 应用程序的页脚中执行以下操作 递归加载public javascripts下的所有脚本
  • 传递给子指令时父指令控制器未定义

    我在这里问了一般性问题这个帖子 https stackoverflow com questions 42814530 pass argument between parent and child directives 我已经通过工作示例得到
  • 将 RavenDB 与 ServiceStack 结合使用

    I read this http www philliphaydon com 2012 06 using nhibernate with servicestack Phillip Haydon 发表的有关如何将 NHibernate Rav
  • ndk-build DUMP_APP_ABI 在 Windows 上返回 2 行

    我无法在 Windows 上调试 android ndk 应用程序 它seems https stackoverflow com questions 20047348 unknown application abi while debug
  • MySQLdb 使用列表作为输入执行许多?

    我想在我的程序中使用executemany一次存储20条记录 这就是文档中所说的 c executemany INSERT INTO breakfast name spam eggs sausage price VALUES s s s s
  • 为什么实体框架在 SELECT 上生成 JOIN

    我在 C 应用程序中使用实体框架 并且使用延迟加载 我们注意到一个查询对我们的 CPU 有着极高的影响 它仅仅计算一个总和 调试实体框架生成的查询时 它会创建一个INNER JOIN SELECT 这不是高性能的 当我手动将查询更改为正确的