English 中文(简体)
惊人的SQL速度增加
原标题:
  • 时间:2008-12-19 10:46:30
  •  标签:

我刚刚发现以下两个选择语句之间的执行计划性能差异巨大:

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%。速度上确实有些差异。当我看到这个时,我感到非常震惊。

I ve always done LEFT(xxx) = yyy as it reads well. I actually found this out by checking the LINQ generated SQL against my hand crafted SQL. I assumed the LIKE command would be slower, but is in fact much much faster.

我的问题是为什么LEFT()比LIKE%..慢呢?它们毕竟是相同的吗?

此外,使用LEFT()是否会影响CPU?

最佳回答

更普遍地说,在查询的 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 
问题回答

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

如果“like”是以前缀锚定模式的形式出现,则通过使用索引来优化它比分析涉及字符串函数的任意表达式要容易得多。

在where子句中使用函数调用会对SQL Server产生巨大的影响,因为它必须计算每一行的结果。另一方面,like是一种内置的语言特性,它经过高度优化。

If you use a function on a column with an index then the db no longer uses the index (at least with Oracle anyway)
So I am guessing that your example field some_string_field has an index on it which doesn t get used for the query with LEFT

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

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

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

这里发生的情况可能是RDBMS不能够在LEFT()谓词上使用索引,但是可以在LIKE上使用索引;或者它在选择更适合的访问方法时做出了错误的选择。

首先,对于一些关系数据库管理系统而言,对列应用函数可能会阻止使用基于索引的访问方法,但这不是普遍的真相,也没有任何逻辑上的原因需要这样做。使用基于索引的访问方法(例如Oracle的完全索引扫描或快速索引扫描)可能是有益的,但在某些情况下,数据库管理系统无法在基于函数谓词的上下文中执行该操作。

其次,优化器可能会在估计不同可用访问方法的优势时出现算术错误。假设系统可以执行基于索引的访问方法,则首先必须估计将与谓词匹配的行数,可以使用表格统计数据、列统计数据、解析时对数据进行抽样或使用启发式规则(例如“假定5%的行将匹配”). 然后,它必须评估完整的表扫描或可用的基于索引的方法的相对成本。有时会出现算术错误,有时统计数据会误导或不准确,有时启发式规则对于数据集不合适。

关键点是要意识到一些问题:

  1. What operations can your RDBMS support?
  2. What would be the most appropriate operation in the case you are working with?
  3. Is the system s choice correct?
  4. What can be done to either allow the system to perform a more efficient operation (eg. add a missing not null constraint, update the statistics etc)?

在我的经验中,这不是一项微不足道的任务,通常最好交给专家来处理。或者,另一方面,只需在Stackoverflow上发布问题-我们中的一些人会发现这种东西很迷人,上帝保佑我们。

正如@BradC所提到的,如果您有索引并希望利用它们,则不应在WHERE子句中使用函数。

如果您阅读标题为“当存在索引时,在WHERE子句中使用LIKE而不是LEFT()或SUBSTRING()”的部分(来自这些SQL性能提示),则有更多示例。

如果你有兴趣参加2012版MCSE SQL Server考试,这也透露了你将会遇到的问题。 :-)





相关问题
热门标签