SQL Server中LIKE %search_string% 走索引查找(Index Seek)浅析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL Server中LIKE %search_string% 走索引查找(Index Seek)浅析相关的知识,希望对你有一定的参考价值。

 

在SQL Server的SQL优化过程中,如果遇到WHERE条件中包含LIKE \'%search_string%\'是一件非常头痛的事情。这种情况下,一般要修改业务逻辑或改写SQL才能解决SQL执行计划走索引扫描或全表扫描的问题。最近在优化SQL语句的时候,遇到了一个很有意思的问题。某些使用LIKE \'%\' + @search_string + \'%\'(或者 LIKE @search_string)这样写法的SQL语句的执行计划居然走索引查找(Index Seek)。下面这篇文章来分析一下这个奇怪的现象。

 

首先,我们来看看WHERE查询条件中使用LIKE的几种情况,这些是我们对LIKE的一些常规认识:

 

1: LIKE \'condition%\'

   

    执行计划会走索引查找(Index Seek or Clustered Index Seek)。

   

2:  LIKE \'%condition\'

 

    执行计划会走索引扫描(Index Scan or Clustered Index Scan)或全表扫描(Table Scan)

 

3:  LIKE \'%condition%\'

   

    执行计划会走索引扫描(Index Scan or Clustered Index Scan)或全表扫描(Table Scan)

 

4: LIKE \'condition1%condition%\';

 

    执行计划会走索引查找(Index Seek)

 

下面我们以AdventureWorks2014示例数据库为测试环境(测试环境为SQL Server 2014 SP2),测试上面四种情况,如下所示:

 

clip_image001

 

 

clip_image002

 

 

clip_image003

 

clip_image004

 

 

其实复杂的情况下,LIKE \'search_string%\'也有走索引扫描(Index Scan)的情况,上面情况并不是唯一、绝对的。如下所示

 

在表Person.Person的 rowguid字段上创建有唯一索引AK_Person_rowguid

 

 

clip_image005

 

 

那么我们来看看上面所说的这个特殊案例(这里使用一个现成的案例,懒得构造案例了),如何让LIKE %search_string%走索引查找(Index Seek),这个技巧就是使用变量,如下SQL对比所示:

 

如下所示,表[dbo].[GEN_CUSTOMER]在字段CUSTOMER_CD有聚集索引。

 

 

clip_image006

 

 

可以看到CUSTOMER_CD LIKE \'%\' + @CUSTOMER_CD + \'%\'这样的SQL写法(或者CUSTOMER_CD LIKE @CUSTOMER_CD也可以), 执行计划就走聚集索引查找(Clustered Index Seek)了, 而条件中直接使用CUSTOMER_CD LIKE \'%00630%\' 反而走聚集索引扫描(Clustered Index Scan),另外可以看到实际执行的Cost开销比为4% VS 96% ,初一看,还真的以为第一个执行计划比第二个执行的代价要小很多。但是从IO开销,以及CPU time、elapsed time对比来看,两者几乎没有什么差异。在这个案例中,并不是走索引查找(Index Seek)就真的开销代价小很多。

 

 

clip_image007

 

 

考虑到这里数据量较小,我使用网上的一个脚本,在AdventureWorks2014数据库构造了一个10000000的大表,然后顺便做了一些测试对比

 

CREATE TABLE dbo.TestLIKESearches
(
     ID1         INT
    ,ID2         INT
    ,AString     VARCHAR(100)
    ,Value       INT
    ,PRIMARY KEY (ID1, ID2)
);
 
WITH Tally (n) AS
(
SELECT TOP 10000000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.all_columns a CROSS JOIN sys.all_columns b
)
INSERT INTO dbo.TestLIKESearches
    (ID1, ID2, AString, Value)
SELECT 1+n/500, n%500
    ,CASE WHEN n%500 > 299 THEN
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1) +
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1) +
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1) +
            RIGHT(1000+n%1000, 3) +
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1) +
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1) +
            SUBSTRING(\'abcdefghijklmnopqrstuvwxyz\', 1+ABS(CHECKSUM(NEWID()))%26, 1)
          END
    ,1+ABS(CHECKSUM(NEWID()))%100
FROM Tally;
 
 
CREATE INDEX IX_TestLIKESearches_N1 ON dbo.TestLIKESearches(AString);

 

如下测试所示,在一个大表上面,LIKE @search_string这种SQL写法,IO开销确实要小一些,CPU Time也要小一些。个人多次测试都是这种结果。也就是说对于数据量较大的表,这种SQL写法性能确实要好一些。

 

clip_image008

 

clip_image009

 

 

现在回到最开始那个SQL语句,个人对执行计划有些疑惑,查看执行计划,你会看到优化器对CUSTOMER_CD LIKE \'%\' + @CUSTOMER_CD + \'%\' 进行了转换。如下截图或通过执行计划的XML,你会发现上面转换为使用三个内部函数LikeRangeStart, LikeRangeEnd,  LikeRangeInfo.

 

clip_image010

 

<OutputList>
                    <ColumnReference Column="Expr1007" />
                    <ColumnReference Column="Expr1008" />
                    <ColumnReference Column="Expr1009" />
                  </OutputList>
                  <ComputeScalar>
                    <DefinedValues>
                      <DefinedValue>
                        <ColumnReference Column="Expr1007" />
                        <ScalarOperator ScalarString="LikeRangeStart((N\'%\'+[@CUSTOMER_CD])+N\'%\')">
                          <Identifier>
                            <ColumnReference Column="ConstExpr1004">
                              <ScalarOperator>
                                <Intrinsic FunctionName="LikeRangeStart">
                                  <ScalarOperator>
                                    <Arithmetic Operation="ADD">
                                      <ScalarOperator>
                                        <Arithmetic Operation="ADD">
                                          <ScalarOperator>
                                            <Const ConstValue="N\'%\'" />
                                          </ScalarOperator>
                                          <ScalarOperator>
                                            <Identifier>
                                              <ColumnReference Column="@CUSTOMER_CD" />
                                            </Identifier>
                                          </ScalarOperator>
如何在 SQL Server 查询中同时使用 LIKE 和 NOT LIKE

sql server 中 like 中文不匹配问题

SQL Server中LIKE和PATINDEX的用法

Sql Server LIKE 查询中需要转义哪些字符[重复]

SQL Server参数化SQL语句中的like和in查询的语法(C#)

SQL Server参数化SQL语句中的like和in查询的语法(C#)