在對oracle的SQL優化過程中經常會遇到【like'%abc'】破壞索引的問題,但是如果真有此類需求,該如何在不破壞索引的基礎上進行查詢呢。
[sql] view plain copy sys@mescp> select reverse('123') from dual; REVERSE('123') -------------------------------- 321 1 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 ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=655 Bytes=15720) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST_LIKE' (Cost=2 Card=655Bytes=15720) 2 1 INDEX (RANGE SCAN) OF 'TEST_LIKE__NAME' (NON-UNIQUE) (Cost=2 Card=118) --'開頭和結尾都是 % ,對不起,很難優化' sys@mescp> select * from test_like where object_name like '%%'; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=655 Bytes=15720) 1 0 TABLE ACCESS (FULL) OF 'TEST_LIKE' (Cost=6 Card=655 ytes=15720) -- '以常量結束,直接寫的時候是不能應用index的' sys@mescp> select * from test_like where object_name like '%S'; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=655 Bytes=15720) 1 0 TABLE ACCESS (FULL) OF 'TEST_LIKE' (Cost=6 Card=655 Bytes=15720) --'以常量結束的,加個reverse 函數,又可以用上index了' sys@mescp> select * from test_like where reverse(object_name)like reverse('%AS'); Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=655 Bytes=15720) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST_LIKE' (Cost=2 Card=655 Bytes=15720) 2 1 INDEX (RANGE SCAN) OF 'TEST_LIKE__NAME_REVERSE' (NON-UNIQUE) (Cost=2 Card=118)
了解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');
問題解決。