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中进行不区分大小写的比较的方法.试一试,看看它是否更快.
因此,优化器选择了一种错误的方式来运行查询。
\n\n由于您无法向 SQL 添加优化器提示来强制优化器选择更好的计划,因此我看到了两个选项。
\n\n在 select 中检索/包含的所有列上添加覆盖索引/索引视图 相当可笑,但我认为它会起作用,因为该索引将使优化器轻松选择更好的计划。
始终过早地具体化包含 First、Last 或 Take 的查询。\xc2\xa0 很危险,因为随着数据变大,在本地提取所有数据和执行 First() \xc2\xa0 以及在服务器上使用 Top 进行查询之间的收支平衡点将会改变。
https://groups.google.com/forum/m/#!topic/microsoft.public.sqlserver.server/L2USxkyV1uw
\n\n\n\n\n\n为什么 TOP 或 SET ROWCOUNT 使我的查询如此缓慢?
\n