为什么添加一个不必要的ToList()会大大加快这个LINQ查询的速度?

Joe*_*ool 21 c# sql linq performance sql-server-2012

为什么使用强制实现ToList()使我的查询数量级更快,如果有的话,它应该完全相反?

1)First()立即打电话

    // "Context" is an Entity Framework DB-first model

    var query = from x in Context.Users
                where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
                select x;

    var User = query.First();

    //  ** The above takes 30+ seconds to run **
Run Code Online (Sandbox Code Playgroud)

2)致电First() 致电ToList():

    var query = from x in Context.Users
                where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
                select x;

    var User = query.ToList().First();     // Added ToList() before First()

    // ** Now it takes < 1 second to run! **
Run Code Online (Sandbox Code Playgroud)

更新和解决方案

获取生成的SQL后,唯一的区别是,正如预期的那样,TOP (1)在第一个查询中添加了.正如Andyz Smith在下面的回答中所说,根本原因是SQL Server优化器在这种特殊情况下,在TOP (1)添加时选择了更糟糕的执行计划.因此,这个问题与LINQ(通过添加做了正确的事情TOP (1))无关,而且与SQL Server的特性有关.

Dav*_*ell 11

我只能想到一个原因......为了测试它,你能否删除该Where条款并重新进行测试?如果结果是第一个语句更快,请在此处注释,我将解释原因.

编辑
在LINQ语句Where子句中,您正在使用字符串的.ToLower()方法.我的猜测是LINQ没有为这个方法内置转换为SQL,所以生成的SQL就行了

SELECT *
FROM Users
Run Code Online (Sandbox Code Playgroud)

现在,我们知道LINQ延迟加载,但它也知道,因为它没有评估该WHERE子句,所以需要加载元素来进行比较.

假设
第一个查询是延迟加载结果集中的每个元素.然后进行.ToLower()比较并返回第一个结果.这导致n对服务器的请求和巨大的性能开销.没有看到SQL Tracelog就无法确定.

第二个语句调用ToList,它在执行ToLower比较之前请求批处理SQL,从而只向服务器发出一个请求

替代假设
如果探查器仅显示一个服务器执行,请尝试使用Top 1子句执行相同的查询,并查看它是否需要一段时间.根据这篇文章(为什么在SQL Server中的索引列上做top(1)会变慢?)TOP子句有时会弄乱SQL服务器优化器并使用正确的索引来阻止它.

好奇心编辑
尝试将LINQ更改为

var query = from x in Context.Users
            where x.Username.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase)
            select x;
Run Code Online (Sandbox Code Playgroud)

感谢@Scott找到在LINQ中进行不区分大小写的比较的方法.试一试,看看它是否更快.

  • 不仅`String.ToLower()`不是扩展方法,它还在EF MSDN上列为*规范函数*(http://msdn.microsoft.com/en-us/library/bb738534.aspx) ,这意味着*所有*EF提供商都支持它.Canonical函数在数据存储**执行**(http://msdn.microsoft.com/en-us/library/bb738626.aspx). (3认同)
  • 诅咒你`Ctrl-C` !!!,[这是我给你的链接](http://stackoverflow.com/questions/2994662/entity-framework-and-differences-between-contains-between-sql -and-对象-使用) (2认同)

And*_*ith 0

因此,优化器选择了一种错误的方式来运行查询。

\n\n

由于您无法向 SQL 添加优化器提示来强制优化器选择更好的计划,因此我看到了两个选项。

\n\n
    \n
  1. 在 select 中检索/包含的所有列上添加覆盖索引/索引视图 相当可笑,但我认为它会起作用,因为该索引将使优化器轻松选择更好的计划。

  2. \n
  3. 始终过早地具体化包含 First、Last 或 Take 的查询。\xc2\xa0 很危险,因为随着数据变大,在本地提取所有数据和执行 First() \xc2\xa0 以及在服务器上使用 Top 进行查询之间的收支平衡点将会改变。

  4. \n
\n\n

http://geekswithblogs.net/Martinez/archive/2013/01/30/why-sql-top-may-slow-down-your-query-and-how.aspx

\n\n

https://groups.google.com/forum/m/#!topic/microsoft.public.sqlserver.server/L2USxkyV1uw

\n\n

http://connect.microsoft.com/SQLServer/feedback/details/781990/top-1-is-not-considered-as-a-factor-for-query-optimization

\n\n

TOP 减慢查询速度

\n\n

为什么 TOP 或 SET ROWCOUNT 使我的查询如此缓慢?

\n