c# - 为什么添加不必要的 ToList() 会大大加快此 LINQ 查询的速度?

标签 c# sql performance linq 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 **

2) 调用 First() after 调用 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! **

更新和解决方案

得到生成的SQL后,唯一不同的是,如预期的那样,在第一个查询中添加了TOP(1)。作为Andyz Smith在他下面的回答中说,根本原因是 SQL Server 优化器,在这种特殊情况下,在添加 TOP (1) 时选择了一个更差的执行计划。因此,该问题与 LINQ 无关(它通过添加 TOP (1) 做了正确的事情)并且与 SQL Server 的特性有关。

最佳答案

我只能想到一个原因... 要测试它,您能否删除 Where 子句并重新运行测试?如果结果是第一个语句更快,请在此处评论,我将解释原因。

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

SELECT *
FROM Users

现在,我们知道 LINQ 延迟加载,但它也知道,由于它没有计算 WHERE 子句,它需要加载元素来进行比较。

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

第二条语句调用ToList,在做ToLower比较前先请求一个批处理SQL,结果只向服务器请求一次

备择假设
如果探查器只显示一个服务器执行,请尝试使用 Top 1 子句执行相同的查询,看看是否需要那么长时间。根据这篇文章 (Why is doing a top(1) on an indexed column in SQL Server slow?),TOP 子句有时会干扰 SQL 服务器优化器并使用正确的索引停止它。

好奇编辑
尝试将 LINQ 更改为

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

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

关于c# - 为什么添加不必要的 ToList() 会大大加快此 LINQ 查询的速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18337103/

相关文章:

c# - 如何在新实现的接口(interface)或基类之间做出决定?

sql - 相同的查询使用不同的索引?

c++ - OpenMP中的每个线程执行相同数量的工作是否正常?

python - 如何有效地将函数应用于数据框中的每一行

c# - 如何使用 C# 在 MVC 布局 View 中编辑 CSS 类

c# - C 夏普日期时间格式

c# - Mentalis 安全套接字库连接已关闭

sql - 正则表达式中的 Mysql 字段名称

php - 在 PHP 中显示 SQL 表并比较值

Java获取匹配范围的最快方法