以通配符(%)开始的like字符串,走索引

时间:2021-08-08 21:01:42

在对oracle的SQL优化过程中经常会遇到【like'%abc'】破坏索引的问题,但是如果真有此类需求,该如何在不破坏索引的基础上进行查询呢。

[sql] view plain copy
sys@mescp> select reverse('') from dual; REVERSE('')
-------------------------------- row selected. sys@mescp> create table test_like as select object_id,object_name from dba_objects; Table created. sys@mescp> create index test_like__name on test_like(object_name); Index created. sys@mescp> create index test_like__name_reverse on test_like(reverse(object_name)); Index created. sys@mescp> analyze table test_like compute statistics for table for all indexes; Table analyzed. sys@mescp> set autotrace trace exp -- '常量开头的like , 会利用index ,没问题…… ' sys@mescp> select * from test_like where object_name like AS%'; Execution Plan ----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes=)
TABLE ACCESS (BY INDEX ROWID) OF 'TEST_LIKE' (Cost= Card=655Bytes=)
INDEX (RANGE SCAN) OF 'TEST_LIKE__NAME' (NON-UNIQUE) (Cost= Card=)
--'开头和结尾都是 % ,对不起,很难优化'
sys@mescp> select * from test_like where object_name like '%%'; Execution Plan ----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes=)
TABLE ACCESS (FULL) OF 'TEST_LIKE' (Cost= Card= ytes=) -- '以常量结束,直接写的时候是不能应用index的'
sys@mescp> select * from test_like where object_name like '%S';
Execution Plan
----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes=) TABLE ACCESS (FULL) OF 'TEST_LIKE' (Cost= Card= Bytes=)
--'以常量结束的,加个reverse 函数,又可以用上index了' sys@mescp> select * from test_like where reverse(object_name)like reverse('%AS');
Execution Plan ---------------------------------------------------------- SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes=)
TABLE ACCESS (BY INDEX ROWID) OF 'TEST_LIKE' (Cost= Card= Bytes=) INDEX (RANGE SCAN) OF 'TEST_LIKE__NAME_REVERSE' (NON-UNIQUE) (Cost= Card=)

了解SQL的同学,都知道,like关键字可以走索引,只要字符串不是以通配符(%)开始。

如果类似 like "%xxx" 的sql,如何走索引呢?基于REVERSE()函数来创建一个函数索引。

1、准备数据:

CREATE TABLE jka AS SELECT ROWNUM id, dbms_random.string('x',10) v FROM dual CONNECT BY LEVEL <= 10000;

2、创建原始索引:

CREATE INDEX jka_normal ON jka (v);

BEGIN
dbms_stats.gather_table_stats
(USER
,'JKA'
,estimate_percent=>100
,cascade=>TRUE);
END;

3、以下SQL基于jka_normal索引走范围扫描:

SELECT * FROM jka WHERE v LIKE 'ABC%';

4、但是下面的SQL将走全表扫描(不会使用索引):

SELECT * FROM jka WHERE v LIKE '%ABC';

5、现在,创建一个函数索引(不要与反向索引[REVERSE INDEX]混淆):

CREATE INDEX jka_reverse ON jka(REVERSE(v));

BEGIN
dbms_stats.gather_table_stats
(USER
,'JKA'
,estimate_percent=>100
,cascade=>TRUE);
END;

6、下面的SQL将基于jka_reverse索引走范围扫描:

SELECT * FROM jka WHERE REVERSE(v) LIKE REVERSE('%ABC');

问题解决。