LIKE使用索引,CHARINDEX不使用索引吗?


22

这个问题与我的旧问题有关。以下查询需要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) 

在一些文章中,我看到使用索引CASTCHARINDEX不会从中受益。也有一些文章说使用LIKE '%abc%'将不会从索引中受益,而LIKE 'abc%'将会:

http://bytes.com/topic/sql-server/answers/81467-using-charindex-vs-like-where /programming/803783/sql-server-index-any-improvement-for -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%'

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

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

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

我尝试重写要使用的查询CHARINDEX,但是性能仍然很慢。为什么CHARINDEXLIKE查询中没有从索引中受益呢?

使用查询CHARINDEX

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

执行计划:

在此处输入图片说明

使用查询LIKE

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE[Company].dbo.[customer].[Phone no] LIKE '%9000413237%'

执行计划:

像查询计划

Answers:


28

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

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

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

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

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

对于LIKE,它的条件以一个通配符开始,SQL Server可以进行局部扫描索引而不是扫描整个事情的。例如,LIKE 'A%可以通过仅测试索引记录>= 'A'< 'B'(正确的边界值取决于排序规则)来正确评估。

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

相比之下,LIKE '%A由于我们不知道从哪里开始或结束,因此不能转变为部分扫描。任何记录都可以以结尾'A',因此我们无法改善扫描整个索引并逐行测试每一行的能力。

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

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

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

不同的估计值足以使优化器选择的聚集索引扫描CHARINDEX和具有查找的非聚集索引扫描LIKE。如果您强制CHARINDEX查询使用带提示的非聚集索引,您将获得与相同的计划LIKE,并且性能将大致相同:

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

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

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


16

SQL Server以尝试的形式维护字符串列中子字符串的统计信息,这些LIKE查询可用于查询但不能用于CHARINDEX

有关更多信息,请参见“ 字符串摘要统计”部分。

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

WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 

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

LIKE查询(在您的情况下)估计将与该谓词匹配的行要少得多。

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

该计划极不可能以30%的估算来选择。SQL Server将认为扫描整个聚集索引并避免进行大量查找会比较便宜。有关更多示例,请参见本文的转折点


我不清楚你的解释。您是说使用like比charindex更好吗?
IT研究员

3
@ITresearcher-是的,可能的是,它不只是简单地猜测将匹配条件(30%)的行数,而是可以查看提供的LIKE模式和字符串摘要统计信息,并得出更准确的估计值。有了这些武器,它可能会选择其他更合适的计划。
马丁·史密斯

3
...或者在“最坏的情况”下,采用相同的计划。
亚伦·伯特兰
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.