C# LINQ嵌套组性能
我有一个从sql compact数据库提取数据的完整外部联接查询(我使用EF6进行映射): 如您所见,我使用嵌套组来构造结果。 有趣的是,如果我删除AsEnumerable()调用,查询执行时间将增加3.5倍:~210ms vs~60ms。当它第一次运行时,差异要大得多:39000(!)毫秒与13000毫秒 我的问题是:C# LINQ嵌套组性能,c#,performance,linq,entity-framework,grouping,C#,Performance,Linq,Entity Framework,Grouping,我有一个从sql compact数据库提取数据的完整外部联接查询(我使用EF6进行映射): 如您所见,我使用嵌套组来构造结果。 有趣的是,如果我删除AsEnumerable()调用,查询执行时间将增加3.5倍:~210ms vs~60ms。当它第一次运行时,差异要大得多:39000(!)毫秒与13000毫秒 我的问题是: 我做错了什么,也许这些分组应该以不同的方式进行 为什么第一次执行要花这么多时间?我知道应该建立表达式树等等,但是39秒 在我的例子中,为什么linq到db比linq到实体慢?如
AsEnumerable()
将实现IEnumerable
的类型转换为IEnumerable
本身
阅读本主题
当序列实现IEnumerable
时,可以使用AsEnumerable(IEnumerable)
在查询实现之间进行选择,但也有一组不同的公共查询方法可用。例如,给定一个通用类Table
,该类实现了IEnumerable
,并具有自己的方法,如Where
、Select
、和SelectMany
,调用Where
将调用Table
的publicWhere
方法。表示数据库表的Table
类型可以有一个Where
方法,该方法将谓词参数作为表达式树,并将树转换为SQL以供远程执行。如果不需要远程执行,例如因为谓词调用本地方法,则可以使用AsEnumerable
方法隐藏自定义方法,而不是使标准查询运算符可用
首先调用
AsEnumerable()
时,它不会将LINQ转换为SQL,而是在枚举表时将表加载到内存中。因为现在它被加载到内存中,所以执行速度更快 要回答您的三个问题:
也许这些分组应该以不同的方式进行
不需要。如果需要嵌套分组,则只能通过分组中的分组来实现
您可以一次按多个字段分组:
from entry in left.Union(right)
select new
{
...
} into e
group e by new
{
e.Date.Year,
Quartal = (e.Date.Month - 1) / 3 + 1,
e.Date.Month,
contract = e.Contract.extNo
} into grp
select new
{
Year = grp.Key,
Quartal = grp.Key,
Month = grp.Key,
Contracts = from x in grp
select new
{
ExtNo = month.Key,
Entries = contract,
}
}
这将从生成的查询中删除大量复杂性,因此,如果不使用AsEnumerable()
,查询速度可能会(快得多)。但结果却大不相同:一个扁平的组(一行中的年份、四分之一等),而不是嵌套的组
为什么第一次执行要花这么多时间
因为生成的SQL查询可能非常复杂,数据库引擎的查询优化器无法找到快速执行路径
3a。在我的例子中,为什么linq到db比linq到实体慢
因为,显然,在这种情况下,首先将数据提取到内存中,然后通过LINQ对对象进行分组要高效得多。如果left
和right
表示或多或少复杂的查询,这种影响将更为显著。在这种情况下,生成的SQL可能会变得非常臃肿,因为它必须在一条语句中处理两个复杂源,这可能会导致许多重复的相同子查询。通过外包分组,数据库可能只剩下一个相对简单的查询,当然,内存中的分组永远不会受到SQL查询复杂性的影响
3b。如果可能的话,在处理之前从数据库加载数据是否通常比较慢和更好
不,不一般。我甚至会说,几乎从来没有。在这种情况下,这是因为(正如我所看到的)您不过滤数据。但是,如果aseneumerable()
之前的部分返回数百万条记录,然后应用过滤,那么不使用aseneumerable()
的查询可能会快得多,因为过滤是在数据库中完成的
因此,您应该始终关注生成的SQL。期望EF总是生成一个超级优化的SQL语句是不现实的。它几乎永远不会。它的主要关注点是正确性(它在这方面做得非常出色),性能是次要的。开发人员的工作是使LINQ to实体和LINQ to对象作为一个灵活的团队一起工作。可计算的在分组之前将数据放入内存。删除它意味着要对数据库运行多个子查询,这会导致数据库运行速度变慢。顺便说一下,下次,最好一次只问一个问题。StackOverflow喜欢用一个答案可能会冒泡出来作为答案的方式回答问题。如果有三个人回答你的三个问题,那就很难了。
from entry in left.Union(right)
select new
{
...
} into e
group e by new
{
e.Date.Year,
Quartal = (e.Date.Month - 1) / 3 + 1,
e.Date.Month,
contract = e.Contract.extNo
} into grp
select new
{
Year = grp.Key,
Quartal = grp.Key,
Month = grp.Key,
Contracts = from x in grp
select new
{
ExtNo = month.Key,
Entries = contract,
}
}