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

时间:2012-11-06 19:12:37

标签: c# mysql linq entity-framework entity-framework-5

为什么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和实体框架与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语句时,它永远不会超过一秒。也许我还有别的东西在继续;我问'因为我不是这样做DB ...

更新3:解释声明

实体框架已生成

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

一个班轮

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

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

解决方案

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

6 个答案:

答案 0 :(得分:7)

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

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

results = results.OrderBy(x=> x.StartTime).Take(limit);

答案 1 :(得分:3)

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

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

编辑:我对此进行了测试,但我发现我的测试中出现了错误,导致其无效。测试结果已删除。

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

我偶然发现了这篇文章,因为我遇到了同样的问题。我已经花了几天时间跟踪它,这只是在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必须提取每条记录才能执行订单生成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

答案 5 :(得分:-2)

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

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