欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-01-28 17:31:34
在SQL Server的SQL优化过程中,如果遇到WHERE条件中包含LIKE '%search_string%'是一件非常头痛的事情。这种情况下,一般要修改业务逻辑或改写SQL才能解决SQL执行计划走索引扫描或全表扫描的问题。最近在优化SQL语句的时候,遇到了一个很有意思的问题。某些使用LIKE ... ......

 

在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),测试上面四种情况,如下所示:

 

 

 

 

 

 

 

 

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

 

在表person.person的 rowguid字段上创建有唯一索引ak_person_rowguid

 

 

 

 

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

 

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

 

 

 

 

可以看到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)就真的开销代价小很多。

 

 

 

 

考虑到这里数据量较小,我使用网上的一个脚本,在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写法性能确实要好一些。

 

 

 

 

现在回到最开始那个sql语句,个人对执行计划有些疑惑,查看执行计划,你会看到优化器对customer_cd like '%' + @customer_cd + '%' 进行了转换。如下截图或通过执行计划的xml,你会发现上面转换为使用三个内部函数likerangestart, likerangeend,  likerangeinfo.

 

 

<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>
                                        </arithmetic>
                                      </scalaroperator>
                                      <scalaroperator>
                                        <const constvalue="n'%'" />
                                      </scalaroperator>
                                    </arithmetic>
                                  </scalaroperator>
                                  <scalaroperator>
                                    <const constvalue="" />
                                  </scalaroperator>
                                </intrinsic>
                              </scalaroperator>
                            </columnreference>
                          </identifier>
                        </scalaroperator>
                      </definedvalue>
                      <definedvalue>
                        <columnreference column="expr1008" />
                        <scalaroperator scalarstring="likerangeend((n'%'+[@customer_cd])+n'%')">
                          <identifier>
                            <columnreference column="constexpr1005">
                              <scalaroperator>
                                <intrinsic functionname="likerangeend">
                                  <scalaroperator>
                                    <arithmetic operation="add">
                                      <scalaroperator>
                                        <arithmetic operation="add">
                                          <scalaroperator>
                                            <const constvalue="n'%'" />
                                          </scalaroperator>
                                          <scalaroperator>
                                            <identifier>
                                              <columnreference column="@customer_cd" />
                                            </identifier>
                                          </scalaroperator>
                                        </arithmetic>
                                      </scalaroperator>
                                      <scalaroperator>
                                        <const constvalue="n'%'" />
                                      </scalaroperator>
                                    </arithmetic>
                                  </scalaroperator>
                                  <scalaroperator>
                                    <const constvalue="" />
                                  </scalaroperator>
                                </intrinsic>
                              </scalaroperator>
                            </columnreference>
                          </identifier>
                        </scalaroperator>
                      </definedvalue>
                      <definedvalue>
                        <columnreference column="expr1009" />
                        <scalaroperator scalarstring="likerangeinfo((n'%'+[@customer_cd])+n'%')">
                          <identifier>
                            <columnreference column="constexpr1006">
                              <scalaroperator>
                                <intrinsic functionname="likerangeinfo">
                                  <scalaroperator>
                                    <arithmetic operation="add">
                                      <scalaroperator>
                                        <arithmetic operation="add">
                                          <scalaroperator>
                                            <const constvalue="n'%'" />
                &nb

                    
                
(0)
打赏 SQL Server中LIKE %search_string% 走索引查找(Index Seek)浅析 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

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