LIKE 使用索引,CHARINDEX 不使用?

IT *_*her 22 performance index sql-server-2008-r2 query-performance

这个问题与我的旧问题有关。执行以下查询需要 10 到 15 秒:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE (Charindex('123456789',CAST([company].dbo.[customer].[Phone no] AS VARCHAR(MAX)))>0) 
Run Code Online (Sandbox Code Playgroud)

在一些文章中,我看到使用CAST并且CHARINDEX不会从索引中受益。还有一些文章说 usingLIKE '%abc%'不会从索引中受益,而LIKE 'abc%'会:

http://bytes.com/topic/sql-server/answers/81467-using-charindex-vs-like-where /sf/ask/56264841/ -like-queries http://www.sqlservercentral.com/Forums/Topic186262-8-1.aspx#bm186568

在我的情况下,我可以将查询重写为:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE [company].dbo.[customer].[Phone no]  LIKE '%123456789%'
Run Code Online (Sandbox Code Playgroud)

此查询提供与前一个相同的输出。我为 column 创建了一个非聚集索引Phone no。当我执行此查询时,它仅在1 秒内运行。与之前的14 秒相比,这是一个巨大的变化。

如何LIKE '%123456789%'从索引中受益?

为什么列出的文章声明它不会提高性能?

我尝试重写查询以使用CHARINDEX,但性能仍然很慢。为什么不像查询那样CHARINDEX从索引中受益LIKE

查询使用CHARINDEX

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 
Run Code Online (Sandbox Code Playgroud)

执行计划:

在此处输入图片说明

查询使用LIKE

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE[Company].dbo.[customer].[Phone no] LIKE '%9000413237%'
Run Code Online (Sandbox Code Playgroud)

执行计划:

LIKE 查询计划

Pau*_*ite 28

LIKE '%123456789%' 如何从索引中受益?

只有一点点。查询处理器可以扫描整个非聚集索引来寻找匹配项,而不是整个表(聚集索引)。非聚集索引通常比它们构建的表小,因此扫描非聚集索引可能会更快。

不利的一面是,查询所需的未包含在非聚集索引定义中的任何列都必须在基表中逐行查找。

优化器根据成本估计,在扫描表(聚集索引)和使用查找扫描非聚集索引之间做出决定。估计成本在很大程度上取决于优化器希望LIKECHARINDEX谓词选择多少行。

为什么列出的文章声明它不会提高性能?

对于以通配符开头的LIKE条件,SQL Server 可以执行索引的部分扫描,而不是扫描整个内容。例如,可以通过仅测试索引记录和(确切的边界值取决于整理)来正确评估。LIKE 'A%>= 'A'< 'B'

这种查询可以利用b-tree索引的查找能力:我们可以直接>= 'A'使用b-tree查找第一条记录,然后按索引键顺序向前扫描,直到找到一条未通过< 'B'测试的记录。由于我们只需要将LIKE测试应用于较少的行,因此性能通常会更好。

相比之下,LIKE '%A不能变成局部扫描,因为我们不知道从哪里开始或结束;任何记录都可能以 结尾'A',因此我们无法改进扫描整个索引并单独测试每一行。

我尝试重写查询以使用CHARINDEX,但性能仍然很慢。为什么不像CHARINDEXLIKE 查询那样从索引中受益?

在这两种情况下,查询优化器在扫描表(聚集索引)和扫描非聚集索引(带查找)之间有相同的选择

根据成本估算在两者之间做出选择。碰巧的是,SQL Server 可能会对这两种方法产生不同的估计。对于LIKE查询的形式,估计可以使用特殊的字符串统计来产生合理准确的估计。该CHARINDEX > 0表单根据猜测生成估计值。

不同的估计足以使优化器CHARINDEXLIKE. 如果您强制CHARINDEX查询使用带有提示的非聚集索引,您将获得与 for 相同的计划LIKE,并且性能将大致相同:

SELECT
    [Customer name],
    [Sl_No],
    [Id]
FROM dbo.customer WITH (INDEX (f))
WHERE 
    CHARINDEX('9000413237', [Phone no]) >0;
Run Code Online (Sandbox Code Playgroud)

两种方法在运行时处理的行数相同,只是LIKE在这种情况下表单会产生更准确的估计,因此查询优化器会选择更好的计划。

如果您发现自己LIKE %thing%经常需要搜索,您可能需要考虑我在 SQL Server中的Trigram 通配符字符串搜索中写的一种技术。


Mar*_*ith 16

SQL Server维护子上的统计数据,字符串列的形式尝试由是可用的LIKE查询,但不能被CHARINDEX

有关这方面的更多信息,请参阅字符串摘要统计信息部分。

几个重要的警告是,任何通配符的转义都必须使用专有的方括号技术而不是ESCAPE关键字来完成,并且对于长度超过 80 个字符的字符串,仅使用前 40 个字符和后 40 个字符。

WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 
Run Code Online (Sandbox Code Playgroud)

将仅使用不等式谓词的标准猜测,即将返回 30% 的行。

LIKE查询(你的情况)大概估计要少得多行将匹配谓语。

请注意,前导通配符仍会阻止索引查找。仍会扫描整个索引,但它使用比聚集索引窄的不同索引。较窄的索引不涵盖查询使用的所有列,因此第二个计划需要键查找来检索丢失的列。

以 30% 的估计值选择此计划的可能性极小。SQL Server 会认为扫描整个聚集索引并避免那么多查找的成本更低。有关其他示例,请参阅有关引爆点的这篇文章。

  • @ITresearcher - 是的,潜在地,它可以查看提供的“LIKE”模式和字符串摘要统计信息并得出更准确的估计值,而不是仅仅使用与条件匹配的行数(`30%`)的全面猜测。有了它,它可能会选择一个不同的、更合适的计划。 (3认同)
  • ……或者,在“最坏的情况”下,同样的计划。 (3认同)