2008-12-19 53 views
13

我刚刚发现了以下两个选择语句之间的执行计划表现是很不同的:令人惊讶的SQL的速度增加

select * from your_large_table 
where LEFT(some_string_field, 4) = '2505' 

select * from your_large_table 
where some_string_field like '2505%' 

执行计划分别为98%和2%。然后,速度上的差异。当我看到它时,我感到震惊。

我总是做LEFT(xxx)='yyy',因为它读得很好。 我实际上通过检查LINQ生成的SQL与我手工制作的SQL来发现这一点。我认为LIKE命令会比较慢,但事实上要快得多。

我的问题是为什么LEFT()比LIKE'%..'慢。他们完全相同?

此外,是否有使用LEFT()命中CPU?

回答

23

更一般地说,您不应该在查询中的WHERE子句的左侧使用函数。如果这样做,SQL将不会使用索引 - 它必须评估表中每一行的函数。我们的目标是确保您的where子句是“Sargable

一些其他的例子:

Bad: Select ... WHERE isNull(FullName,'') = 'Ed Jones' 
Fixed: Select ... WHERE ((FullName = 'Ed Jones') OR (FullName IS NULL)) 

Bad: Select ... WHERE SUBSTRING(DealerName,4) = 'Ford' 
Fixed: Select ... WHERE DealerName Like 'Ford%' 

Bad: Select ... WHERE DateDiff(mm,OrderDate,GetDate()) >= 30 
Fixed: Select ... WHERE OrderDate < DateAdd(mm,-30,GetDate()) 

Bad: Select ... WHERE Year(OrderDate) = 2003 
Fixed: Select ... WHERE OrderDate >= '2003-1-1' AND OrderDate < '2004-1-1' 
7

在where子句中使用函数调用有很大的影响,因为SQL Server必须计算每行的结果。另一方面,like是高度优化的内置语言功能。

3

如果您对列使用函数的索引则DB不再使用的索引(至少与Oracle反正)
所以我猜测,你的榜样场“some_string_field”有其没有按它的索引't用于'LEFT'的查询

+0

这不完全正确。该指数仍然可以使用,但可能以不同的方式。如果谓词上的预期匹配数量很小,并且索引在物理上比tabe小得多,则可以利用索引全或全速扫描。 – 2008-12-19 17:07:02

+0

有趣的是,谢谢你的信息 – hamishmcn 2008-12-22 18:57:08

17

它看起来像表达式LEFT(some_string_field,4)针对全表扫描的每一行进行评估,而“like”表达式将使用该索引。

优化“like”使用索引(如果它是前锚定模式)比分析涉及字符串函数的任意表达式更容易优化。

1

你为什么说它们是相同的?他们可能会解决同样的问题,但他们的方法是不同的。至少它看起来像这样...

使用LEFT的查询优化了测试,因为它已经知道前缀的长度等,所以在C/C++/...程序中或没有索引,使用LEFT来实现某种LIKE行为的算法将是最快的。但是与大多数非声明性语言相比,在SQL数据库上,为您完成了很多操作优化。例如LIKE可能是通过首先查找%符号来实现的,并且如果注意到%是字符串中的最后一个字符,那么可以按照与使用LEFT相同的方式对查询进行优化,但直接使用索引。

所以,我认为你的毕竟是,他们的方法可能是一致的。唯一的区别是数据库服务器可以在使用LIKE的查询中使用索引,因为没有函数将列值转换为WHERE子句中未知的值。

+0

'%'是LIKE,Fred的通配符。 – 2008-12-19 13:46:27

+0

嗯,我不能不同意,我的观点是db可能已经优化了“a like'xxx%'”为“left(a,3)='xxx'”但这并不重要,因为数据库可以使用索引,所以无论如何它总是会更快。 – FredV 2008-12-22 13:25:35

1

这里发生了什么或者是该RDBMS是不能够使用左侧的索引()谓词并且能够在LIKE上使用它,或者它只是在错误的调用中更合适的访问方法。

首先,对某些RDBMS来说,将一个函数应用于列可能会阻止使用基于索引的访问方法,但这不是一个普遍的事实,也没有任何逻辑理由说明它需要。基于索引的访问方法(如Oracle的全索引扫描或快速全索引扫描)可能是有益的,但在某些情况下,RDBMS无法在基于函数的谓词的上下文中进行操作。其次,优化器在估计不同可用访问方法的好处时可能会错误地计算错误。假设系统可以执行基于索引的访问方法,首先要根据表的统计数据,列的统计数据,在解析时对数据进行抽样来估计与谓词匹配的行数,或者使用启发式规则(例如,“假定5%的行将匹配”)。然后,它必须评估全表扫描的相对成本或可用的基于索引的方法。有时会导致算法错误,有时统计数据会有误导性或不准确,有时启发式规则不适合数据集。

关键的一点是要注意的一些问题:

  1. 什么操作可以在你的RDBMS的支持?
  2. 在您使用的 案件中,最合适的操作是什么?
  3. 系统的选择是否正确?
  4. 可以做些什么来允许系统执行更高效的操作(例如,添加缺少非空约束,更新统计信息等)?

根据我的经验,这不是一项简单的任务,而且最好留给专家处理。或者,另一方面,只需将问题发布到Stackoverflow - 我们中的一些人发现这个东西很有趣,狗帮助我们。

1

正如@BradC所提到的,如果你有索引并且想要利用它们,你不应该在WHERE子句中使用函数。

如果您阅读标题为“使用LIKE而不是WHERE子句中的LEFT()或SUBSTRING()时出现索引的部分”from these SQL Performance Tips,还有更多示例。

这也暗示了如果您有兴趣也可以在MCSE SQL Server 2012 exams上遇到的问题。 :-)

相关问题