• 函数使得索引列失效


    在索引列上使用函数使得索引失效的是常见的索引失效原因之一,因此尽可能的避免在索引列上使用函数。尽管可以使用基于函数的索引来
    解决索引失效的问题,但如此一来带来的比如磁盘空间的占用以及列上过多的索引导致DML性能的下降。本文描述的是一个索引列上使用函数使
    其失效的案例。

    一、数据版本与原始语句及相关信息
      1.版本信息    

    [sql] view plain copy
     
     print?
    1. SQL> select * from v$version;                                         
    2.                                                                       
    3. BANNER                                                                
    4. ----------------------------------------------------------------      
    5. Oracle Database 10g Release 10.2.0.3.0 - 64bit Production             
    6. PL/SQL Release 10.2.0.3.0 - Production                                
    7. CORE    10.2.0.3.0      Production                                    
    8. TNS for Linux: Version 10.2.0.3.0 - Production                        
    9. NLSRTL Version 10.2.0.3.0 - Production                                

      2.原始语句与其执行计划   

    [sql] view plain copy
     
     print?
    1. SQL> set autotrace traceonly exp;                                                                   
    2.                                                                                                     
    3. SELECT acc_num,                                                                                     
    4.        curr_cd,                                                                                       
    5.        DECODE('20110728',                                                                             
    6.               (SELECT TO_CHAR(LAST_DAY(TO_DATE('20110728', 'YYYYMMDD')),                                  
    7.                               'YYYYMMDD')                                                                         
    8.                FROM   DUAL),                                                                              
    9.               0,                                                                                          
    10.               adj_credit_int_lv1_amt + adj_credit_int_lv2_amt -                                           
    11.               adj_debit_int_lv1_amt - adj_debit_int_lv2_amt) AS interest                                  
    12. FROM   acc_pos_int_tbl ACC_POS_INT_TBL1                                                             
    13. WHERE  SUBSTR(business_date, 1, 6) = SUBSTR('20110728', 1, 6)                                       
    14.        AND business_date <= '20110728';                                                               
    15.                                                                                                     
    16. Execution Plan                                                                                      
    17. ----------------------------------------------------------                                          
    18. Plan hash value: 3114115399                                                                         
    19.                                                                                                     
    20. -------------------------------------------------------------------------------------               
    21. | Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |               
    22. -------------------------------------------------------------------------------------               
    23. |   0 | SELECT STATEMENT  |                 |   336K|    12M| 96399   (1)| 00:19:17 |               
    24. |   1 |  FAST DUAL        |                 |     1 |       |     2   (0)| 00:00:01 |               
    25. |*  2 |  TABLE ACCESS FULL| ACC_POS_INT_TBL |   336K|    12M| 96399   (1)| 00:19:17 |               
    26. -------------------------------------------------------------------------------------               
    27.                                                                                                     
    28. Predicate Information (identified by operation id):                                                 
    29. ---------------------------------------------------                                                 
    30.                                                                                                     
    31.    2 - filter(SUBSTR("BUSINESS_DATE",1,6)='201107' AND                                              
    32.               "BUSINESS_DATE"<='20110728')                                                          

        从执行计划可以看出,SQL语句使用了全表扫描,而where 子句中只有唯一的一列business_date

      3.表上的索引信息     

    [sql] view plain copy
     
     print?
    1. SQL> set autotrace off;                                                                                    
    2. SQL> set linesize 190                                                                                      
    3. SQL> @Idx_Info                                                                                             
    4. Enter value for owner: goex_admin                                                                          
    5. old  10:           AND owner = upper('&owner')                                                             
    6. new  10:           AND owner = upper('goex_admin')                                                         
    7. Enter value for table_name: ACC_POS_INT_TBL                                                                
    8. old  11:           AND a.table_name = upper('&table_name')                                                 
    9. new  11:           AND a.table_name = upper('ACC_POS_INT_TBL')                                             
    10.                                                                                                            
    11. TABLE_NAME         INDEX_NAME               COL_NAM              CL_POS STATUS   IDX_TYP         DSCD      
    12. ------------------ ------------------------ -------------------- ------ -------- --------------- ----      
    13. ACC_POS_INT_TBL    ACC_POS_INT_10DIG_IDX    SYS_NC00032$              1 VALID    FUNCTION-BASED  ASC       
    14.                                                                                  NORMAL                    
    15.                                                                                                            
    16. ACC_POS_INT_TBL    ACC_POS_INT_10DIG_IDX    BUSINESS_DATE             2 VALID    FUNCTION-BASED  ASC       
    17.                                                                                  NORMAL                    
    18.                                                                                                            
    19. ACC_POS_INT_TBL    ACC_POS_INT_10DIG_IDX    CURR_CD                   3 VALID    FUNCTION-BASED  ASC       
    20.                                                                                  NORMAL                    
    21.                                                                                                            
    22. ACC_POS_INT_TBL    PK_ACC_POS_INT_TBL       ACC_NUM                   1 VALID    NORMAL          ASC       
    23. ACC_POS_INT_TBL    PK_ACC_POS_INT_TBL       BUSINESS_DATE             2 VALID    NORMAL          ASC       

        从索引的情况上来看有一个基于主键的索引包含了BUSINESS_DATE列,而查询语句并没有走索引而是选择的全表扫描,而且预估所返回
        的行Rows与bytes也是大的惊人,cost的值96399,接近10W。

    二、分析与改造SQL语句
      1.原始的SQL语句分析
           SQL语句中where子句的business_date列实现对记录过滤
           business_date <= '20110728'条件不会限制索引的使用
           SUBSTR(business_date, 1, 6) = SUBSTR('20110728', 1, 6)使用了SUBSTR函数,限制了优化器选择索引
           基于business_date列来建立索引函数,从已存在的索引来看,必要性不大
       
      2.改造SQL语句
        SUBSTR(business_date, 1, 6) = SUBSTR('20110728', 1, 6)的实质是等于当月,即限制返回的行为从2011.7.1日至2011.7.28
        因此其返回的记录大于等于2011.7.1,且小于2011.7.28
        做如下改造
         business_date >=to_char(last_day(add_months(to_date('20110728','yyyymmdd'),-1)) + 1,'yyyymmdd')
       
      3.改造后的SQL语句   

    [sql] view plain copy
     
     print?
    1. SELECT acc_num,                                                                  
    2.        curr_cd,                                                                    
    3.        DECODE('20110728',                                                          
    4.               (SELECT TO_CHAR(LAST_DAY(TO_DATE('20110728', 'YYYYMMDD')),               
    5.                               'YYYYMMDD')                                                      
    6.                FROM   DUAL),                                                           
    7.               0,                                                                       
    8.               adj_credit_int_lv1_amt + adj_credit_int_lv2_amt -                        
    9.               adj_debit_int_lv1_amt - adj_debit_int_lv2_amt) AS interest               
    10. FROM   acc_pos_int_tbl ACC_POS_INT_TBL1                                          
    11. WHERE  business_date >=                                                          
    12.        to_char(last_day(add_months(to_date('20110728', 'yyyymmdd'), -1)) + 1,      
    13.                'yyyymmdd')                                                             
    14.        AND business_date <= '20110728';                                               

       4.改造后的执行计划  

    [sql] view plain copy
     
     print?
    1. Execution Plan                                                                                                 
    2. ----------------------------------------------------------                                                     
    3. Plan hash value: 66267922                                                                                      
    4.                                                                                                                
    5. --------------------------------------------------------------------------------------------------             
    6. | Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |             
    7. --------------------------------------------------------------------------------------------------             
    8. |   0 | SELECT STATEMENT            |                    |  1065K|    39M| 75043   (1)| 00:15:01 |             
    9. |   1 |  FAST DUAL                  |                    |     1 |       |     2   (0)| 00:00:01 |             
    10. |   2 |  TABLE ACCESS BY INDEX ROWID| ACC_POS_INT_TBL    |  1065K|    39M| 75043   (1)| 00:15:01 |             
    11. |*  3 |   INDEX SKIP SCAN           | PK_ACC_POS_INT_TBL | 33730 |       | 41180   (1)| 00:08:15 |             
    12. --------------------------------------------------------------------------------------------------             
    13.                                                                                                                
    14. Predicate Information (identified by operation id):                                                            
    15. ---------------------------------------------------                                                            
    16.                                                                                                                
    17.    3 - access("BUSINESS_DATE">='20110701' AND "BUSINESS_DATE"<='20110728')                                     
    18.        filter("BUSINESS_DATE">='20110701' AND "BUSINESS_DATE"<='20110728')                                     

        改造后可以看到SQL语句的执行计划已经由原来的全表扫描改为执行INDEX SKIP SCAN,但其cost也并没有降低多少

    三、进一步分析
      1.表的相关信息  

    [sql] view plain copy
     
     print?
    1. SQL> @Tab_Stat                                                                                          
    2. Enter value for input_table_name: ACC_POS_INT_TBL                                                       
    3. old  11: WHERE  table_name = upper('&input_table_name')                                                 
    4. new  11: WHERE  table_name = upper('ACC_POS_INT_TBL')                                                   
    5. Enter value for input_owner: goex_admin                                                                 
    6. old  12:           AND owner = upper('&input_owner')                                                    
    7. new  12:           AND owner = upper('goex_admin')                                                      
    8.                                                                                                         
    9.   NUM_ROWS       BLKS    EM_BLKS  AVG_SPACE  CHAIN_CNT AVG_ROW_LEN AVG_ROWS_PER_BLOCK LST_ANLY  STA     
    10. ---------- ---------- ---------- ---------- ---------- ----------- ------------------ --------- ---     
    11.   33659947     437206       1322        855          0          99                 77 27-SEP-11 NO      

      2.索引的相关信息 

    [sql] view plain copy
     
     print?
    1. SQL> @Idx_Stat                                                                                                         
    2. Enter value for input_table_name: ACC_POS_INT_TBL                                                                      
    3. old  11: WHERE  table_name = upper('&input_table_name')                                                                
    4. new  11: WHERE  table_name = upper('ACC_POS_INT_TBL')                                                                  
    5. Enter value for input_owner: goex_admin                                                                                
    6. old  12:           AND owner = upper('&input_owner')                                                                   
    7. new  12:           AND owner = upper('goex_admin')                                                                     
    8.                                                                                                                        
    9. BLEV IDX_NAME                          LF_BLKS   DST_KEYS   NUM_ROWS LF_PER_KEY DAT_BLK_PER_KEY   CLUS_FCT LST_ANLY    
    10. ---- ------------------------------ ---------- ---------- ---------- ---------- --------------- ---------- ---------   
    11.    3 PK_ACC_POS_INT_TBL                 155658   33777720   33777720          1               1   33777447 27-SEP-11   
    12.    3 ACC_POS_INT_10DIG_IDX              160247   32850596   32850596          1               1   32763921 27-SEP-11   

      3.尝试在BUSINESS_DATE列上创建索引   

    [sql] view plain copy
     
     print?
    1. SQL> create index I_ACC_POS_INT_TBL_BS_DT on ACC_POS_INT_TBL(BUSINESS_DATE) tablespace tbs_tmp nologging;               
    2.                                                                                                                         
    3. Index created.                                                                                                          
    4.                                                                                                                         
    5. SQL> @Idx_Stat                                                                                                          
    6. Enter value for input_table_name: ACC_POS_INT_TBL                                                                       
    7. old  11: WHERE  table_name = upper('&input_table_name')                                                                 
    8. new  11: WHERE  table_name = upper('ACC_POS_INT_TBL')                                                                   
    9. Enter value for input_owner: goex_admin                                                                                 
    10. old  12:           AND owner = upper('&input_owner')                                                                    
    11. new  12:           AND owner = upper('goex_admin')                                                                      
    12.                                                                                                                         
    13. BLEV IDX_NAME                          LF_BLKS   DST_KEYS   NUM_ROWS LF_PER_KEY DAT_BLK_PER_KEY   CLUS_FCT LST_ANLY     
    14. ---- ------------------------------ ---------- ---------- ---------- ---------- --------------- ---------- ---------    
    15.    2 I_ACC_POS_INT_TBL_BS_DT             93761        908   33659855        103             506     460007 30-SEP-11    
    16.    3 PK_ACC_POS_INT_TBL                 155658   33777720   33777720          1               1   33777447 27-SEP-11    
    17.    3 ACC_POS_INT_10DIG_IDX              160247   32850596   32850596          1               1   32763921 27-SEP-11    

      建立索引后聚簇因子较小,差不多接近表上块的数量
      
      4.使用新创建索引后的执行计划   

    [sql] view plain copy
     
     print?
    1. Execution Plan                                                                                                 
    2. ----------------------------------------------------------                                                     
    3. Plan hash value: 2183566226                                                                                    
    4.                                                                                                                
    5. -------------------------------------------------------------------------------------------------------        
    6. | Id  | Operation                   | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |        
    7. -------------------------------------------------------------------------------------------------------        
    8. |   0 | SELECT STATEMENT            |                         |  1065K|    39M| 17586   (1)| 00:03:32 |        
    9. |   1 |  FAST DUAL                  |                         |     1 |       |     2   (0)| 00:00:01 |        
    10. |   2 |  TABLE ACCESS BY INDEX ROWID| ACC_POS_INT_TBL         |  1065K|    39M| 17586   (1)| 00:03:32 |        
    11. |*  3 |   INDEX RANGE SCAN          | I_ACC_POS_INT_TBL_BS_DT |  1065K|       |  2984   (1)| 00:00:36 |        
    12. -------------------------------------------------------------------------------------------------------        
    13.                                                                                                                
    14. Predicate Information (identified by operation id):                                                            
    15. ---------------------------------------------------                                                            
    16.                                                                                                                
    17.    3 - access("BUSINESS_DATE">='20110701' AND "BUSINESS_DATE"<='20110728')                                     

      从上面的执行计划看出,SQL语句已经选择了新建的索引
      尽管返回的rows,bytes没有明显的变化,但cost已经少了近7倍。

  • 相关阅读:
    升级WP应用时注意的问题——WMAppManifest.xml
    MVVM Light (Part 4)
    Windows Phone 7的About模板——Your Last About Dialog(2)支持多语言
    MVVM Light 开始
    在ScheduledTaskAgent中使用HttpWebRequest
    年会抽奖程序 支持单次单个抽奖和单次多个抽奖,自定义抽奖设置
    WIndows Phone 7的MVVM Light框架
    MVVM Light (Part 3)
    MVVM Light 行为
    [转]如何在设计中应用颜色搭配技巧
  • 原文地址:https://www.cnblogs.com/AmilyWilly/p/5488711.html
Copyright © 2020-2023  润新知