> 数据库 > Oracle >

逆置与Like匹配

Like匹配是SQL语句的一种重要功能。Like结合通配符使用可以实现模糊查询,解决字符串匹配问题。当查询的数据集合比较大的时候,我们常常希望借助索引来加快检索效率。
 
默认的索引行为
 
like查询是否会执行索引路径,很大程度上取决于使用的通配符结构。如果我们仔细分析下B*树索引的结构,就可以判断出:‘XXX%’类型的匹配结构,是可以执行索引搜索的。
 
通过下面实验证明:
 
//构造原始数据集合
SQL> create table t as select * from dba_objects;
 
Table created
//在owner列上建索引
SQL> create index idx_t_owner on t(owner);
 
Index created
 
//收集统计信息
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
 
PL/SQL procedure successfully completed
 
 
此时,我们使用通配符进行查询。
 
SQL> select * from t where owner like 'PE%';
 
已选择139行。
 
已用时间:  00: 00: 00.06
 
执行计划
----------------------------------------------------------
Plan hash value: 1516787156
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |  2231 |   204K|    67   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T           |  2231 |   204K|    67   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_OWNER |  2231 |       |     7   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OWNER" LIKE 'PE%')
       filter("OWNER" LIKE 'PE%')
 
统计信息
----------------------------------------------------------
        381  recursive calls
          0  db block gets
         82  consistent gets
          0  physical reads
          0  redo size
      16321  bytes sent via SQL*Net to client
        484  bytes received via SQL*Net from client
         11  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
        139  rows processed
 
发现,虽然使用了like,但是还是执行了索引路径。
 
结论:在匹配字段通配符不是在开头的情况下,like走索引列的效率比较高,往往优化器会选择这条路径。
 
 
通配符在开头
 
在一些情况下,我们需要进行通配符在开头的检索。这种情况下,使用一般的索引一般是不会被选择为实际执行路径的。
 
SQL> select * from t where owner like '%AT';
 
已选择139行。
 
已用时间:  00: 00: 00.12
 
执行计划
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  2666 |   255K|   165   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| T    |  2666 |   255K|   165   (2)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OWNER" LIKE '%AT')
统计信息
----------------------------------------------------------
        838  recursive calls
          0  db block gets
        912  consistent gets
          1  physical reads
          0  redo size
      10424  bytes sent via SQL*Net to client
        484  bytes received via SQL*Net from client
         11  SQL*Net roundtrips to/from client
         24  sorts (memory)
          0  sorts (disk)
        139  rows processed
 
Oracle优化器选择了全表扫描作为执行路径。应该说,在索引和全表扫描两种方式下,Oracle选择了全表扫描,原因是因为索引路径的成本Cost更高。
 
 
思路:如果匹配的字符串前面不存在通配符,走索引的路径成本相对较低了。那么,怎么将其转化到后面呢?使用逆置的函数索引。
 
函数索引的原理,就是利用函数处理出一个隐藏列,再对隐藏列进行索引处理。
 
SQL> create index idx_t_fun on t(reverse(owner));
 
Index created
 
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
 
PL/SQL procedure successfully completed
 
同时,在搜索的时候,要将匹配的字符串也逆置处理。例如,如果要处理匹配的字符串是’%AT’。那么进行函数匹配的时候,就要使用‘TA%’。
 
SQL> select * from t where reverse(owner) like reverse('%AT');
 
已选择139行。
 
已用时间:  00: 00: 00.04
 
执行计划
----------------------------------------------------------
Plan hash value: 3350694433
 
-----------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |  2132 |   204K|    64   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T         |  2132 |   204K|    64   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_FUN |  2132 |       |     7   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(REVERSE("OWNER") LIKE 'TA%')
       filter(REVERSE("OWNER") LIKE 'TA%')
 
统计信息
----------------------------------------------------------
        117  recursive calls
          0  db block gets
         52  consistent gets
          0  physical reads
          0  redo size
      10424  bytes sent via SQL*Net to client
        484  bytes received via SQL*Net from client
         11  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
        139  rows processed
 
修改之后,发现执行路径已经可以走索引路径了。而且执行时间也缩小到原来全表扫描的1/3。
(责任编辑:IT)