• Oracle索引字段发生隐式转换仍然能够使用索引


    Oracle索引字段发生隐式转换仍然能够使用索引

    前言

    最近在优化SQL过程中,发现执行计划中,某张表使用了索引但是根据执行计划谓词信息部分,发现该索引字段被隐式转换了。

    在我以前的认知中,索引字段被隐式转换后是用不了索引的,因此自己构造如下实验做测试。

    环境构造

    14:43:47 SYS@zkm(17)> create user zkm identified by oracle;                              
    
    User created.
    
    Elapsed: 00:00:00.05
    14:43:54 SYS@zkm(17)> grant dba to zkm;
    
    Grant succeeded.
    
    Elapsed: 00:00:00.01
    14:43:59 SYS@zkm(17)> create table zkm.t ( n1 char(20),n2 char(20),n3 char(30));
    
    Table created.
    
    Elapsed: 00:00:00.10
    14:44:07 SYS@zkm(17)> insert into zkm.t values('1','1','1');
    
    1 row created.
    
    Elapsed: 00:00:00.01
    14:44:10 SYS@zkm(17)> insert into zkm.t values('2','2','2');
    
    1 row created.
    
    Elapsed: 00:00:00.00
    14:44:14 SYS@zkm(17)> commit;
    
    Commit complete.
    
    Elapsed: 00:00:00.00
    14:44:16 SYS@zkm(17)> create index zkm.idx on zkm.t(n1);
    
    Index created.
    
    Elapsed: 00:00:00.05
    14:44:21 SYS@zkm(17)> analyze table zkm.t compute statistics;
    
    Table analyzed.
    
    Elapsed: 00:00:00.11

    过程

    14:48:45 SYS@zkm(17)>  conn zkm/oracle
    Connected.
    14:48:52 ZKM@zkm(17)> set line 500
    14:48:57 ZKM@zkm(17)> set autot traceo exp
    14:49:03 ZKM@zkm(17)> select /*+ index(t IDX) */ * from zkm.t where n1=1;
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 1601196873
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      |     1 |    70 |     3   (0)| 00:00:01 |
    |*  1 |  TABLE ACCESS FULL| T    |     1 |    70 |     3   (0)| 00:00:01 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(TO_NUMBER("N1")=1)
    
    14:49:11 ZKM@zkm(17)> select /*+ index(t IDX) */ * from zkm.t where n1='1';
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 2770274160
    
    ------------------------------------------------------------------------------------
    | Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |      |     1 |    70 |     2   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| T    |     1 |    70 |     2   (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | IDX  |     1 |       |     1   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - access("N1"='1')

    这个时候发现并不能模拟出想要的结果。

    当n1=1的时候,根据谓词信息可以得知,此时n1被做了隐式转换。

    于此同时,并没法使用索引了。

    研究了一会没啥头绪,于是将生产遇到的问题在群里问问看大神们。

    总结下来的意思就是,隐式转换后索引失效是对于索引范围扫描、索引唯一扫描之类的,但对于索引全扫描以及快速索引全扫描是不影响的。

    于是,根据下表,hint换成index_ffs再试试看。

    索引 索引范围扫描 索引跳跃扫描 索引快速全扫描
    index index_rs index_ss index_ffs
    index_asc no_index_rs no_index_ss no_index_ffs
    index_desc index_rs_asc index_ss_asc index_asc
      index_rs_desc index_ss_desc index_desc

    需要注意一点的是,根据《基于Oracle的SQL调优》一书中P620处指出,

    索引快速全扫描能成立的前提条件是SELECT语句中所有的查询列都存在于目标索引中,即通过扫描索引就可以得到所有的查询列而不用回表。

    于是:

    15:11:35 ZKM@zkm(17)> select /*+ INDEX_FFS(a IDX) */ n1 from zkm.t a where n1=1;
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 1601196873
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      |     1 |    20 |     3   (0)| 00:00:01 |
    |*  1 |  TABLE ACCESS FULL| T    |     1 |    20 |     3   (0)| 00:00:01 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(TO_NUMBER("N1")=1)
    
    15:11:36 ZKM@zkm(17)> select /*+ INDEX_FFS(a IDX) */ n1 from zkm.t a where n1='1';
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 3451708965
    
    -----------------------------------------------------------------------------
    | Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |      |     1 |    20 |     2   (0)| 00:00:01 |
    |*  1 |  INDEX FAST FULL SCAN| IDX  |     1 |    20 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("N1"='1')

    还是不行,后续继续在群里抛出问题,然后中午吃饭午休了。

    ......
    一觉醒来,丁俊大牛已经找出问题所在,要求该字段为非空。

    15:21:13 ZKM@zkm(17)> alter table zkm.t modify n1 not null;
    
    Table altered.
    
    Elapsed: 00:00:00.16
    15:21:26 ZKM@zkm(17)> select /*+ INDEX_FFS(a IDX) */ n1 from zkm.t a where n1=1;
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 3451708965
    
    -----------------------------------------------------------------------------
    | Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |      |     1 |    20 |     2   (0)| 00:00:01 |
    |*  1 |  INDEX FAST FULL SCAN| IDX  |     1 |    20 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(TO_NUMBER("N1")=1)
       
    15:22:25 ZKM@zkm(17)> select /*+ INDEX_FFS(a IDX) */ n1 from zkm.t a where n1='1';
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 3451708965
    
    -----------------------------------------------------------------------------
    | Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |      |     1 |    20 |     2   (0)| 00:00:01 |
    |*  1 |  INDEX FAST FULL SCAN| IDX  |     1 |    20 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("N1"='1')

    果然可以了,这个其实和select count(*) from zkm.t能不能走索引的前置条件一样。

    如下:

    15:36:21 ZKM@zkm(17)> alter table t modify n1 null;
    
    Table altered.
    
    Elapsed: 00:00:00.01
    15:39:41 ZKM@zkm(17)> select count(*) from zkm.t;
    Elapsed: 00:00:00.00
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 2966233522
    
    -------------------------------------------------------------------
    | Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
    -------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |
    |   1 |  SORT AGGREGATE    |      |     1 |            |          |
    |   2 |   TABLE ACCESS FULL| T    |     2 |     3   (0)| 00:00:01 |
    -------------------------------------------------------------------
    
    15:39:45 ZKM@zkm(17)> select count(*) from zkm.t where n1 is not null;
    Elapsed: 00:00:00.01
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 387325252
    
    -------------------------------------------------------------------------
    | Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------
    |   0 | SELECT STATEMENT |      |     1 |    20 |     1   (0)| 00:00:01 |
    |   1 |  SORT AGGREGATE  |      |     1 |    20 |            |          |
    |*  2 |   INDEX FULL SCAN| IDX  |     2 |    40 |     1   (0)| 00:00:01 |
    -------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - filter("N1" IS NOT NULL)

    不过这里Oracle比较不智能,这里n1=1已经可以排除掉null的情况了,应该是不需要特地设置n1为null或者加n1 is not null条件。

    收获

    1. 具体索引的执行方式的hint,参见上表。
    2. 索引快速全扫描的限制。
    3. 隐式转换并非每次都限制索引的使用。
    4. 关于索引全扫描,网上有资料说和索引快速全扫描一样需要查询列都在目标索引中,其实不用。

    第4点验证:

    15:52:56 ZKM@zkm(17)> select /*+ index(t IDX) */ * from zkm.t where n1=1;
    Elapsed: 00:00:00.01
    
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 2518247465
    
    ------------------------------------------------------------------------------------
    | Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |      |     1 |    70 |     2   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| T    |     1 |    70 |     2   (0)| 00:00:01 |
    |*  2 |   INDEX FULL SCAN           | IDX  |     1 |       |     1   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - filter(TO_NUMBER("N1")=1)
  • 相关阅读:
    macOS npm install -g npm失败提示无权限
    js中的展开运算符
    vite启动项目
    export与export default区别
    vue3新特性拾遗
    isRef, isReactive 与 isReadonly的原理
    shallowReactive与reactive的原理浅析
    shallowReadonly 与 readonly原理浅析
    shallowRef与ref的原理浅析
    设计模式-资料相关
  • 原文地址:https://www.cnblogs.com/PiscesCanon/p/13925955.html
Copyright © 2020-2023  润新知