Skip to Main Content
  • Questions
  • Why cost for TABLE ACCESS BY INDEX ROWID to high for only one row

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Nguyen Luong.

Asked: September 01, 2015 - 5:29 pm UTC

Last updated: October 17, 2019 - 7:49 am UTC

Version: 11g Enterprise Edition Release 11.2.0.4.0 - 64bit

Viewed 10K+ times! This question is

You Asked

Dear Tom,
I have problem with query on table have function base index.
create index :
create index customer_idx_idno on Customer (lower(id_no)) ; --- id_no varchar2(40)

Query 1: execute time 0.031s but cost 5,149, 1 row return
select * from customer where LOWER (id_no) = '46192145'
--------------------------------------------------------------------------------
Plan
SELECT STATEMENT  ALL_ROWSCost: 5,149  Bytes: 2,440,656  Cardinality: 11,964  Partition #: 0    
  2 TABLE ACCESS BY INDEX ROWID CM_POS.CUSTOMER Cost: 5,149  Bytes: 2,440,656  Cardinality: 11,964  Partition #: 0   
      1 INDEX RANGE SCAN CM_POS.CUST_ID_NO_IDX Cost: 3  Bytes: 0  Cardinality: 5,171  Partition #: 0  

Query 2: execute time 0.484s cost 9,821, result = 0
-- there are index on : sub.contract_id, con.contract_id(pk), con.cust_id, cust.cust_id (pk), sub.sub_id(pk), stock.sub_id
SELECT   COUNT ( * )
  FROM           sub_mb sub
             INNER JOIN
                 contract con
             ON sub.contract_id = con.contract_id AND con.status IN (1, 2, 9)
         INNER JOIN
             customer cust
         ON con.cust_id = cust.cust_id AND LOWER (cust.id_no) = '46192145'
 WHERE   sub.status IN (1, 2, 9)
         AND EXISTS (SELECT   1
                       FROM   sub_stock_model_rel stock
                      WHERE   sub.sub_id = stock.sub_id AND stock.status = 1)

--------------------------------------------------------------------------------
Plan
SELECT STATEMENT  ALL_ROWSCost: 9,256  Bytes: 127  Cardinality: 1  Partition #: 0           
 17 SORT AGGREGATE  Cost: 0  Bytes: 127  Cardinality: 1  Partition #: 0          
  16 HASH JOIN SEMI  Cost: 9,256  Bytes: 1,561,846  Cardinality: 12,298  Partition #: 0         
   14 HASH JOIN  Cost: 8,732  Bytes: 1,461,076  Cardinality: 12,382  Partition #: 0        
    12 HASH JOIN  Cost: 6,613  Bytes: 1,272,874  Cardinality: 12,358  Partition #: 0       
     4 VIEW CM_POS.index$_join$_004 Cost: 2,827  Bytes: 1,052,832  Cardinality: 11,964  Partition #: 0      
      3 HASH JOIN  Cost: 0  Bytes: 0  Cardinality: 0  Partition #: 0     
       1 INDEX RANGE SCAN CM_POS.CUST_ID_NO_IDX Cost: 3  Bytes: 1,052,832  Cardinality: 11,964  Partition #: 0    
       2 INDEX FAST FULL SCAN CM_POS.CUSTOMER_PK Cost: 3,526  Bytes: 1,052,832  Cardinality: 11,964  Partition #: 0    
     11 VIEW CM_POS.index$_join$_002 Cost: 3,786  Bytes: 2,717,070  Cardinality: 181,138  Partition #: 0      
      10 HASH JOIN  Cost: 0  Bytes: 0  Cardinality: 0  Partition #: 0     
       8 HASH JOIN  Cost: 0  Bytes: 0  Cardinality: 0  Partition #: 0    
        6 INLIST ITERATOR  Cost: 0  Bytes: 0  Cardinality: 0  Partition #: 0   
         5 INDEX RANGE SCAN CM_POS.IX68_CONTRACT Cost: 493  Bytes: 2,717,070  Cardinality: 181,138  Partition #: 0  
        7 INDEX FAST FULL SCAN CM_POS.IX67_CONTRACT Cost: 917  Bytes: 2,717,070  Cardinality: 181,138  Partition #: 0   
       9 INDEX FAST FULL SCAN CM_POS.PK_CONTRACT Cost: 882  Bytes: 2,717,070  Cardinality: 181,138  Partition #: 0    
    13 TABLE ACCESS FULL CM_POS.SUB_MB Cost: 2,118  Bytes: 2,676,195  Cardinality: 178,413  Partition #: 0       
   15 TABLE ACCESS FULL CM_POS.SUB_STOCK_MODEL_REL Cost: 523  Bytes: 2,021,832  Cardinality: 224,648  Partition #: 0        


Please tell me why query with function base index is to slow what is problem in my query(query 2), how to optimize it. I waiting for your anwser!

and Chris said...

Just looking at the simpler query, you say it returns one row, but the optimizer thinks it is going to return over 5000 rows. (Hence the similar cost figure)

That should be your first point of investigation, because it suggests the statistics are either incorrect, out of date, missing or there is insufficient statistics to make a good judgement on the cardinality of that expression.

Use USER_TAB_COLS to check the statistics for hidden columns (which is basically what a function based index column will be). It may well be the case that you might need to create a histogram on this column. Then repeat your tests to see if the cardinality estimates are closer to reality.

Without a test case script that we can run here to see for ourselves (check the question guidelines), then we really cant say for sure.


Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

I resolved this problem

Nguyen Luong Thai Duc, September 02, 2015 - 2:15 am UTC

Hi Tom,
I do it follow your guide:
select column_name,hidden_column,virtual_column,  histogram
from user_tab_cols where table_name = 'CUSTOMER';
--------------------------------------------------------
column_name  hidden_column virtual_column  histogram
SYS_NC00061$ YES       YES       NONE


i try to analyze this column of table
analyze table CUSTOMER compute statistics for columns SYS_NC00061$

Now seelect again
select column_name,hidden_column,virtual_column,  histogram
from user_tab_cols where table_name = 'CUSTOMER';
--------------------------------------------------------
column_name  hidden_column virtual_column  histogram
SYS_NC00061$ YES       YES    HEIGHT BALANCED


Now, my query cost only 12.
Thank you so much!
Connor McDonald
September 03, 2015 - 5:33 am UTC

I'm glad we helped.

Dont forget - use DBMS_STATS not ANALYZE.

Rob lasonder, December 14, 2018 - 8:28 am UTC

I rarely find the Ask Tom answers very useful. In most situations the questions are not answered, but some other suggestions are made or they doubt the initial approach of the person asking the question. After Burleson, this is the second worst site.
Connor McDonald
December 17, 2018 - 2:09 am UTC

We welcome all feedback, good and bad.

Although I find it interesting you opted for doing it on this particular question.

- They had a query that was slow

- We provided some analysis (the cost looked wrong)
- We suggested potential cause (stats incorrect or insufficient)
- We suggested how to look for evidence of that (user_tab_cols)

- They look at user_tab_cols
- Found the missing stats
- Solved the problem

I'm a bit at a loss of how you came to your conclusion

optimization and stats

Dave, October 16, 2019 - 3:37 pm UTC

question, what if stats are the same but optimizer_features_enable changes the execution time? I have a trace and reviewing it. I have noticed some diff's in the order of execution of accesses. I stumbled onto this question when noticing
TABLE ACCESS BY INDEX ROWID BATCHED --12.2
TABLE ACCESS BY INDEX ROWID --8.1.7

/*+ optimizer_features_enable('12.2.0.1') */ -- over 2 min--2vnarjugk1cwt
/*+ optimizer_features_enable('11.2.0.4') */ --80 sec--79ymx8jxknj64
/*+ optimizer_features_enable('11.2.0.3') */ -- 80 sec
/*+ optimizer_features_enable('11.2.0.2') */ -- 60 sec
/*+ optimizer_features_enable('11.2.0.1') */ -- 72 sec


/*+ optimizer_features_enable('10.2.0.3') */-- 68 sec
/*+ optimizer_features_enable('10.1.0.4') */ --116
/*+ optimizer_features_enable('9.2.0.8') */ --89 sec
/*+ optimizer_features_enable('9.2.0') */ -- 120 sec
/*+ optimizer_features_enable('9.0.0') */ -- 120 sec
/*+ optimizer_features_enable('8.1.7') */ --5 sec--7j6vzyjgwmujf
/*+ optimizer_features_enable('8.1.6') */ -- 6 sec
/*+ optimizer_features_enable('8.1.0') */ -- 9 sec

I have been told by oracle support many times its the sql but how can i justify that when it runs great in dev(OFE 12.2.0.1 Linux ASM) but not in stage and prod (UNIX file system). I feel like it could be stats but even if it is stats how could I prove which one it is. I have used DBMS_STATS/cascade true with auto sample size, tried 100% so i dont know. Should I try taking stats from dev? The explain plans are almost identical.

Thanks for any direction you can give me

Dave

Chris Saxon
October 17, 2019 - 7:49 am UTC

Sure playing with optimizer_features_enable can change execution times. After all, that controls which optimizations are available, which change in each version.

BATCHED index access was added in 12c. As it's only moving the OFE down from 9 -> 8 that sees a big performance gain, I doubt the change in index access is what's the root cause of change.

But really you shouldn't be fiddling with OFE:

https://mikedietrichde.com/2013/12/16/why-you-shouldnt-set-optimizer_features_enable/

The explain plans are almost identical

So what are the differences? And are you comparing execution or explain plans? Because it's execution plans with runtime stats that you really need to understand what's going on.

Seeing the differences in the rows estimated and processed in the different environments will help you figure out why performance is different.

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.