当前位置:   article > 正文

SQL中用到LIKE模糊检索的几种优化场景

sql like模糊查询匹配多个字段 如何优化

SQL开发中经常会碰到使用LIKE模糊检索的场景,'%'的位置,可能影响索引的正常使用,看到刘老师公众号的一篇文章,介绍了相关场景的改造策略,非常受用,推荐阅读。

测试表t1,object_name列创建索引,

  1. create table t1 as select * from dba_objects;
  2. create index idx_t1_01 on t1(object_name);

场景一,'%'在后

select object_name from t1 where object_name like 'BISAL%';

明显能使用索引,但是要注意,这种情况下,'%'前字符串越短,索引的选择性就可能越差,

6d699650c3e214cac2ce0eed3961ad71.png

场景二,'%'在前

select object_name from t1 where object_name like '%BISAL'

因为这个索引是按照object_name列的正序在索引中组织的,头部的模糊检索,无法直接通过索引定位数据,只是因为检索列只有object_name,所以用到的是索引快速全扫描,实际还是扫描的所有索引叶子节点,

053b762076fc2a20cd5ce3021911cae8.png

为了说明清楚,我们检索object_id列,他不在索引中,

select object_id from t1 where object_name like '%BISAL';

因此,Oracle选择了成本更低的全表扫描,

bc9be80d86a284242aa535a941c9c521.png

作为比对,我们按照'BISAL%',能用到索引的场景测试下,

select object_id from t1 where object_name like 'BISAL%';

可以看到,用到的是索引范围扫描,得到rowid,再回表得到具体的数据,不需要扫描整个索引或者整张表,

00907a60c9562c168c5388d456b87007.png

我们拉回来,如果非得用'%BISAL'检索,可以创建一个object_name列的反向索引,

create index idx_t1_02 on t1(reverse(object_name));

查询语句中LIKE的右值同样使用reverse函数

select object_name from t1 where reverse(object_name) like reverse('%BISAL');

此时,'%BISAL'用到了索引,细心的朋友可能发现执行计划和上面的略有不同,这里多了回表的操作,原因就是索引是按照reverse(object_name)组织的,但是检索的是object_name,因此要根据索引进行回表,

43cffdfb7c32cbc7343738a5457bbf99.png

场景三,前后'%'

例如'%BISAL%',能不能使用索引?

分为三种情况,

(1) ABC始终从字符串开始的某个固定位置出现,可以创建函数索引进行优化。

(2) ABC始终从字符串结尾的某个固定位置出现,可以创建函数组合索引进行优化。

(3) ABC在字符串中位置不固定,可以通过改写SQL进行优化。

第一种情况,ABC始终从字符串开始的某个固定位置出现。

可以通过substr函数截取字符串的功能,创建函数索引。

例如,BISAL从字符串的第五位出现,创建函数索引,

create index idx_t1_03 on t1(substr(object_name, 5, 30));

执行如下SQL,相当于每次都从原字符串的第五位开始截取,

select object_name from t1 where substr(object_name, 5, 30) like 'BISAL%';

可以用到索引,

65815bbfbc741f587cd1fc35a213005e.png

第二种情况,ABC始终从字符串结尾的某个固定位置出现,可以创建函数组合索引进行优化。

相当于需要倒序截取字符串,可以通过reverse和substr组合函数索引,例如BISAL从字符串倒数第五位出现,创建函数索引,

create index idx_t1_04 on t1(reverse(substr(object_name, 1, length(object_name)-4)));

检索的时候,需要用到reverse和substr函数组合,like右值用'%BISAL',就可以实现‘%BISAL%’检索功能,

select object_name from t1 where reverse(substr(object_name, 1, length(object_name)-4)) like reverse ('%BISAL');

第三种情况,ABC在字符串中位置不固定,可以通过改写SQL进行优化。

这种就需要改写,假设object_name存在索引,要求执行如下,

select object_name from t1 where object_name like '%BISAL%';

我们改写成,通过一个子查询,和条件object_name关联,

select object_name from t1 where object_name in (select object_name from t1 where object_name like '%BISAL%');

此时的执行计划,如下所示,索引快速全扫描和索引范围扫描的组合,

8487fce132fcc3d79405f5733d7c541c.png

即使我们检索object_id这个不在索引中的字段,

select object_id from t1 where object_name in (select object_name from t1 where object_name like '%BISAL%');

同样避免了全表扫描,虽然还是要索引快速全扫描,但至少扫描的成本降低了(1/N(索引块数和数据块数的比例)),单就这点来说,表越大,效果可能越明显。但是这种IN改写,如果子查询返回的记录数较少,执行效率就可能提高了N倍,但如果较多,改写的效率,可能和之前相差不大了,

52b9de2afc7c0724f5c8c9f7492fa5bc.png

虽然以上的'%'有各种改造的方案,但至少都得改写一些SQL,所以还是建议,从需求层面,确定使用LIKE模糊检索的场景到底合理不合理,他的非功能指标是否满足要求,不要上来就改,谋定而后动,就可能事半功倍。

近期更新的文章:

短道速滑和速度滑冰科普贴

InnoDB执行delete时到底做了什么?

最近碰到的问题

关于数据治理的读书笔记 - 企业数据治理的“道、法、术、器”

克隆PDB数据库操作

文章分类和索引:

公众号900篇文章分类和索引

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/424491
推荐阅读
相关标签
  

闽ICP备14008679号