前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SQL中用到LIKE模糊检索的几种优化场景

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

作者头像
bisal
发布2022-02-18 21:01:19
1.1K0
发布2022-02-18 21:01:19
举报

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

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

代码语言:javascript
复制
create table t1 as select * from dba_objects;
create index idx_t1_01 on t1(object_name);

场景一,'%'在后

代码语言:javascript
复制
select object_name from t1 where object_name like 'BISAL%';

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

6d699650c3e214cac2ce0eed3961ad71.png
6d699650c3e214cac2ce0eed3961ad71.png

场景二,'%'在前

代码语言:javascript
复制
select object_name from t1 where object_name like '%BISAL'

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

053b762076fc2a20cd5ce3021911cae8.png
053b762076fc2a20cd5ce3021911cae8.png

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

代码语言:javascript
复制
select object_id from t1 where object_name like '%BISAL';

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

bc9be80d86a284242aa535a941c9c521.png
bc9be80d86a284242aa535a941c9c521.png

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

代码语言:javascript
复制
select object_id from t1 where object_name like 'BISAL%';

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

00907a60c9562c168c5388d456b87007.png
00907a60c9562c168c5388d456b87007.png

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

代码语言:javascript
复制
create index idx_t1_02 on t1(reverse(object_name));

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

代码语言:javascript
复制
select object_name from t1 where reverse(object_name) like reverse('%BISAL');

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

43cffdfb7c32cbc7343738a5457bbf99.png
43cffdfb7c32cbc7343738a5457bbf99.png

场景三,前后'%'

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

分为三种情况,

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

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

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

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

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

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

代码语言:javascript
复制
create index idx_t1_03 on t1(substr(object_name, 5, 30));

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

代码语言:javascript
复制
select object_name from t1 where substr(object_name, 5, 30) like 'BISAL%';

可以用到索引,

65815bbfbc741f587cd1fc35a213005e.png
65815bbfbc741f587cd1fc35a213005e.png

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

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

代码语言:javascript
复制
create index idx_t1_04 on t1(reverse(substr(object_name, 1, length(object_name)-4)));

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

代码语言:javascript
复制
select object_name from t1 where reverse(substr(object_name, 1, length(object_name)-4)) like reverse ('%BISAL');

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

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

代码语言:javascript
复制
select object_name from t1 where object_name like '%BISAL%';

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

代码语言:javascript
复制
select object_name from t1 where object_name in (select object_name from t1 where object_name like '%BISAL%');

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

8487fce132fcc3d79405f5733d7c541c.png
8487fce132fcc3d79405f5733d7c541c.png

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

代码语言:javascript
复制
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
52b9de2afc7c0724f5c8c9f7492fa5bc.png

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-02-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档