为什么Entity Framework会生成嵌套的SQL查询?

为什么Entity Framework会生成嵌套的SQL查询?

我有这个代码

var db = new Context(); var result = db.Network.Where(x => x.ServerID == serverId) .OrderBy(x=> x.StartTime) .Take(limit); 

哪个产生了这个! (注意双选语句)

 SELECT `Project1`.`Id`, `Project1`.`ServerID`, `Project1`.`EventId`, `Project1`.`StartTime` FROM (SELECT `Extent1`.`Id`, `Extent1`.`ServerID`, `Extent1`.`EventId`, `Extent1`.`StartTime` FROM `Networkes` AS `Extent1` WHERE `Extent1`.`ServerID` = @p__linq__0) AS `Project1` ORDER BY `Project1`.`StartTime` DESC LIMIT 5 

我应该更改什么才能产生一个选择语句? 我正在使用MySQL和Entity Framework与Code First。

更新

无论传递给OrderBy()方法的参数类型如何,我都有相同的结果。

更新2:定时

 Total Time (hh:mm:ss.ms) 05:34:13.000 Average Time (hh:mm:ss.ms) 25:42.000 Max Time (hh:mm:ss.ms) 51:54.000 Count 13 First Seen Nov 6, 12 19:48:19 Last Seen Nov 6, 12 20:40:22 

原始查询:

 SELECT `Project?`.`Id`, `Project?`.`ServerID`, `Project?`.`EventId`, `Project?`.`StartTime` FROM (SELECT `Extent?`.`Id`, `Extent?`.`ServerID`, `Extent?`.`EventId`, `Extent?`.`StartTime`, FROM `Network` AS `Extent?` WHERE `Extent?`.`ServerID` = ?) AS `Project?` ORDER BY `Project?`.`Starttime` DESC LIMIT ? 

我使用一个程序从MySQL中的当前进程中获取快照。

其他查询同时执行,但是当我将其更改为一个SELECT语句时,它永远不会超过一秒。 也许我还有别的东西在继续; 我问’因为我不是这样的…

更新3:解释声明

entity framework生成

 '1', 'PRIMARY', '', 'ALL', NULL, NULL, NULL, NULL, '46', 'Using filesort' '2', 'DERIVED', 'Extent?', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', '', '45', 'Using where' 

一个class轮

 '1', 'SIMPLE', 'network', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', 'const', '45', 'Using where; Using filesort' 

这来自我的QA环境,因此我上面粘贴的时间与rowcount explain语句无关。 我认为有大约500,000条记录匹配一个服务器ID。

我从MySQL切换到SQL Server。 我不想最终完全重写应用程序层。

这是从表达式树逻辑构建查询的最简单方法。 通常表现不会成为问题。 如果您遇到性能问题,可以尝试这样的方法来恢复实体:

 var results = db.ExecuteStoreQuery( "SELECT Id, ServerID, EventId, StartTime FROM Network WHERE ServerID = @ID", serverId); results = results.OrderBy(x=> x.StartTime).Take(limit); 

我最初的印象是这样做实际上会更有效率,尽管在对MSSQL服务器进行测试时,我得到了<1秒的响应。

使用单个select语句,它会对所有记录( Order By )进行排序,然后将它们过滤到您想要查看的集合( Where ),然后排在前5位( Limit 5或者对我来说, Top 5 )。 在大型表格上,排序占用了大部分时间。 使用嵌套语句,它首先将记录过滤到子集,然后才对其执行昂贵的排序操作。

编辑:我测试了这个,但我意识到我的测试中有一个错误导致它无效。 测试结果已删除。

为什么Entity Framework会生成嵌套查询? 简单的答案是因为entity framework将您的查询表达式分解为表达式树,然后使用该表达式树来构建查询。 树自然地生成嵌套查询表达式(即,子节点生成查询,父节点生成对该查询的查询)。

为什么Entity Framework不能简化查询并按原样编写它? 简单的答案是因为可以进入查询生成引擎的工作量有限,虽然它现在比早期版本更好,但它并不完美,可能永远不会。

所有这一切都表示,您手写的查询与本例中生成的查询EF之间不应存在明显的速度差异。 数据库足够聪明,可以生成在任何一种情况下首先应用WHERE子句的执行计划。

如果要在不使用子选择的情况下让EF生成查询,请在查询中使用常量,而不是变量。

我之前创建了自己的.Where和所有其他LINQ方法,它们首先遍历表达式树并将所有变量,方法调用等转换为Expression.Constant。 这是因为entity framework中的这个问题…

我只是偶然发现了这篇文章,因为我遇到了同样的问题。 我已经花了几天跟踪它,它只是在mysql中生成一个糟糕的查询。

我已经在mysql.com http://bugs.mysql.com/bug.php?id=75272上提交了一个错误

总结一下这个问题:

这个简单的查询

 context.products .Include(x => x.category) .Take(10) .ToList(); 

被翻译成

 SELECT `Limit1`.`C1`, `Limit1`.`id`, `Limit1`.`name`, `Limit1`.`category_id`, `Limit1`.`id1`, `Limit1`.`name1` FROM (SELECT `Extent1`.`id`, `Extent1`.`name`, `Extent1`.`category_id`, `Extent2`.`id` AS `id1`, `Extent2`.`name` AS `name1`, 1 AS `C1` FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id` LIMIT 10) AS `Limit1` 

并表现得很好。 无论如何,外部查询几乎没用。 现在如果我添加一个OrderBy

 context.products .Include(x => x.category) .OrderBy(x => x.id) .Take(10) .ToList(); 

查询更改为

 SELECT `Project1`.`C1`, `Project1`.`id`, `Project1`.`name`, `Project1`.`category_id`, `Project1`.`id1`, `Project1`.`name1` FROM (SELECT `Extent1`.`id`, `Extent1`.`name`, `Extent1`.`category_id`, `Extent2`.`id` AS `id1`, `Extent2`.`name` AS `name1`, 1 AS `C1` FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id`) AS `Project1` ORDER BY `Project1`.`id` ASC LIMIT 10 

哪个是坏的,因为order by在外部查询中。 这意味着MySQL必须提取每条记录才能执行orderby,从而导致using filesort

我确认SQL Server(至少Comapact)不会为相同的代码生成嵌套查询

 SELECT TOP (10) [Extent1].[id] AS [id], [Extent1].[name] AS [name], [Extent1].[category_id] AS [category_id], [Extent2].[id] AS [id1], [Extent2].[name] AS [name1], FROM [products] AS [Extent1] LEFT OUTER JOIN [categories] AS [Extent2] ON [Extent1].[category_id] = [Extent2].[id] ORDER BY [Extent1].[id] ASC 

实际上,entity framework生成的查询很难看,比LINQ 2 SQL少,但仍然很难看。

但是,很可能您的数据库引擎将制定所需的执行计划,并且查询将顺利运行。