挑战:让Linq-to-Entities生成不错的SQL而不需要不必要的连接

时间:2009-05-29 06:14:19

标签: entity-framework linq-to-entities

我最近在msdn的实体框架论坛上遇到了一个问题: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bb72fae4-0709-48f2-8f85-31d0b6a85f68

提出问题的人试图做一个相对简单的查询,包括两个表,一个分组,一个顺序,以及一个使用Linq-to-Entities的聚合。一个非常直截了当的Linq查询,并且在SQL中也很简单 - 人们每天都会尝试做的事情。

然而,当使用Linq-to-Entities时,结果是一个复杂的查询,有很多不必要的连接等。我试过它并且无法让Linq-to-Entities从它生成一个不错的SQL查询,如果使用只是纯粹的Linq对抗EF实体。

从EF中看到了相当多的怪物查询我认为OP(和我,and others)做错了。也许有更好的方法来做到这一点?

所以这是我的挑战:使用the example from the EF forum并对这两个实体使用Linq-to-Entities,是否可以让EF生成一个没有不必要的连接和其他复杂性的SQL查询?

我希望看到EF生成的东西更接近Linq-to-SQL对同类查询的作用,同时仍然使用Linq对抗EF模型。

限制:使用EFv1 .net 3.5 SP1或EFv4(beta 1是可从Microsoft下载的VS2010 / .net4测试版的一部分)。没有CSDL-> SSDL映射技巧,模型'definitionsqueries',存储过程,db-side函数或允许的视图。只是在模型和数据库之间进行简单的1:1映射,以及执行MSDN上原始线程所要求的纯L2E查询。两个实体之间必须存在关联(即我对原始线程的“解决方法#1”答案不是有效的解决方法)

更新: 500pt赏金添加。玩得开心。

更新:如上所述,使用EFv4 / .net 4(β1或更高版本)的解决方案当然有资格获得赏金。如果您使用.net 4postβ1,请包含内部版本号(例如4.0.20605),您使用的L2E查询以及它生成并发送到数据库的SQL。

更新:此问题已在VS2010 / .net 4 beta 2中得到修复。虽然生成的SQL仍有一些[相对无害]额外的嵌套级别,但它不会执行任何操作以前的坚果SQL Server优化器完成后的最终执行计划现在已经尽可能好了。 +++用于负责EFv4的SQL生成部分的家伙和工作人员......

3 个答案:

答案 0 :(得分:2)

如果我担心疯狂的SQL,我就不会在数据库中进行任何分组。我首先用ToList()查询所需的所有数据,同时使用Include函数在一个select中加载所有数据。

这是我的最终结果:

var list = from o in _entities.orderT.Include("personT")
           .Where(p => p.personT.person_id == person_id && 
                       p.personT.created >= fromTime && 
                       p.personT.created <= toTime).ToList()
           group o by new { o.name, o.personT.created.Year, o.personT.created.Month, o.personT.created.Day } into g
           orderby g.Key.name
           select new { g.Key, count = g.Sum(x => x.price) };

这导致更简单的选择:

SELECT 
1 AS [C1], 
[Extent1].[order_id] AS [order_id], 
[Extent1].[name] AS [name], 
[Extent1].[created] AS [created], 
[Extent1].[price] AS [price], 
[Extent4].[person_id] AS [person_id], 
[Extent4].[first_name] AS [first_name], 
[Extent4].[last_name] AS [last_name], 
[Extent4].[created] AS [created1]
FROM    [dbo].[orderT] AS [Extent1]
LEFT OUTER JOIN [dbo].[personT] AS [Extent2] ON [Extent1].[person_id] = [Extent2].[person_id]
INNER JOIN [dbo].[personT] AS [Extent3] ON [Extent1].[person_id] = [Extent3].[person_id]
LEFT OUTER JOIN [dbo].[personT] AS [Extent4] ON [Extent1].[person_id] = [Extent4].[person_id]
WHERE ([Extent1].[person_id] = @p__linq__1) AND ([Extent2].[created] >= @p__linq__2) AND ([Extent3].[created] <= @p__linq__3)

此外,通过提供的示例数据,SQL事件探查器仅注意到SQL调用持续时间增加了3毫秒。

就我个人而言,我认为任何抱怨不喜欢ORM层输出SQL的人应该回到使用存储过程和数据集。他们还没有准备好进化,需要在众所周知的烤箱中度过几年。 :)

答案 1 :(得分:2)

有趣的讨论。到目前为止,我已经使用了2个ORM模型(NHibernate和LINQ-to-Entities)。根据我的经验,总有一行必须放弃ORM来生成SQL并使用存储过程或视图来实现最佳可伸缩查询。话虽如此,我个人认为LINQ在更规范化的数据库上工作得更好,所有嵌套查询/连接都不是主要问题。在某些情况下,为了提高性能或可伸缩性,您必须使用数据库服务器功能(例如,SQL 2008 SE上的索引视图仅适用于查询提示),您根本无法使用ORM(iBatis除外?)。 / p>

假设您不会通过使用linq生成的这些嵌套连接/查询来获得最佳性能或可伸缩性,但请不要忘记LINQ(或NHibernate)在任何项目中提供的优势和开发优势。当然必须有一些优点。

最后,虽然我冒着比较苹果和橙子的风险,但更不用想问:你想要快速的网站开发(asp.net webforms,swing)或更多的HTML控制(asp.net mvc,RoR)?选择最适合您要求的东西。

我的2美分!

答案 2 :(得分:0)

linq生成的SQL非常有效。它可能看起来很笨重,但它考虑到表格和约束等关系。在我看来,你应该盲目地使用linq命令,而不是担心规模。自动生成大型查询有很多好处。它避免了关系约束中的任何滑动,并为故障/异常添加了自己的包装器。

如果您想自己编写SQL并且仍然希望在ORM的范围之后工作,那么请尝试iBatis http://ibatis.apache.org/您必须自己编写SQL并加入,因此它可以让您完全控制后端模型。

就个人而言,只需使用SQLMetal和linq。除非你需要,否则不要担心性能和规模。