为什么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()
方法的参数类型如何,我都会得到相同的结果。
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 ...
实体框架已生成
'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。我不想最终完全重写应用程序层。
答案 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少,但仍然很难看。
但是,很可能您的数据库引擎将制定所需的执行计划,并且查询将顺利运行。