Question

I need advice on the attached Query. The query executes for over an hour and has full table scan as per the Explain Plan. I am fairly new to query tuning and would appriciate some advice. Firstly why would I get a full table scan even though all the columns I use have index created on them. Secondly, is there any possibility where in I can reduce the execution time, all tables accessed are huge and contain millions of records, even then I would like to scope out some options. Appriciate your help.

Query:

select 
    distinct rtrim(a.cod_acct_no)||'|'||
    a.cod_prod||'|'||
    to_char(a.dat_acct_open,'Mon DD YYYY HH:MMAM')||'|'||
    a.cod_acct_title||'|'||
    a.cod_acct_stat||'|'||
    ltrim(to_char(a.amt_od_limit,'99999999999999999990.999999'))||'|'||
    ltrim(to_char(a.bal_book,'99999999999999999990.999999'))||'|'||
    a.flg_idd_auth||'|'||
    a.flg_mnt_status||'|'||
    rtrim(c.cod_acct_no)||'|'||
    c.cod_10||'|'||
    d.nam_branch||'|'||
    d.nam_cc_city||'|'||
    d.nam_cc_state||'|'||
    c.cod_1||'|'||
    c.cod_14||'|'||
    num_14||'|'||
    a.cod_cust||'|'||
    c.cod_last_mnt_chkrid||'|'||
    c.dat_last_mnt||'|'||
    c.ctr_updat_srlno||'|'||       
    c.cod_20||'|'||            
    c.num_16||'|'||
    c.cod_14||'|'||                
    c.num_10  ||'|'||
    a.flg_classif_reqd||'|'||

    (select g.cod_classif_plan_id||'|'||
     g.cod_classif_plan_id
     from
     ac_acct_preferences g 
     where 
     a.cod_acct_no=g.cod_acct_no AND g.FLG_MNT_STATUS = 'A' )||'|'||
    (select e.dat_cam_expiry from  flexprod_host.AC_ACCT_PLAN_CRITERIA e where  a.cod_acct_no=e.cod_acct_no   and e.FLG_MNT_STATUS ='A')||'|'||
    c.cod_23||'|'||
    lpad(trim(a.cod_cc_brn),4,0)||'|'||

    (select min( o.dat_eff)  from ch_acct_od_hist o where a.cod_acct_no=o.cod_acct_no )
from    
    ch_acct_mast a,

    ch_acct_cbr_codes c,
    ba_cc_brn_mast d

where 
    a.flg_mnt_status ='A'

    and c.flg_mnt_status ='A'
    and a.cod_acct_no= c.cod_acct_no(+)
    and a.cod_cc_brn=d.cod_cc_brn 

    and a.cod_prod in (
    299,200,804,863,202,256,814,232,182,844,279,830,802,833,864,
    813,862,178,205,801,235,897,231,187,229,847,164,868,805,207,
    250,837,274,253,831,893,201,809,846,819,820,845,811,843,285,
    894,284,817,832,278,818,810,181,826,867,825,848,871,866,895,
    770,806,827,835,838,881,853,188,816,293,298)

Query Plan:

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4253465430

------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                            |   733K|   125M|       |   468K  (1)|999:59:59 |       |       |
|   1 |  TABLE ACCESS BY INDEX ROWID        | AC_ACCT_PREFERENCES        |     1 |    26 |       |     3   (0)| 00:01:05 |       |       |
|*  2 |   INDEX UNIQUE SCAN                 | IN_AC_ACCT_PREFERENCES_1   |     1 |       |       |     2   (0)| 00:00:43 |       |       |
|   3 |   PARTITION HASH SINGLE             |                            |     1 |    31 |       |     3   (0)| 00:01:05 |   KEY |   KEY |
|   4 |    TABLE ACCESS BY LOCAL INDEX ROWID| AC_ACCT_PLAN_CRITERIA      |     1 |    31 |       |     3   (0)| 00:01:05 |   KEY |   KEY |
|*  5 |     INDEX UNIQUE SCAN               | IN_AC_ACCT_PLAN_CRITERIA_1 |     1 |       |       |     2   (0)| 00:00:43 |   KEY |   KEY |
|   6 |     SORT AGGREGATE                  |                            |     1 |    29 |       |            |          |       |       |
|   7 |      FIRST ROW                      |                            |     1 |    29 |       |     3   (0)| 00:01:05 |       |       |
|*  8 |       INDEX RANGE SCAN (MIN/MAX)    | IN_CH_ACCT_OD_HIST_1       |     1 |    29 |       |     3   (0)| 00:01:05 |       |       |
|   9 |  HASH UNIQUE                        |                            |   733K|   125M|   139M|   468K  (1)|999:59:59 |       |       |
|* 10 |   HASH JOIN                         |                            |   733K|   125M|       |   439K  (1)|999:59:59 |       |       |
|* 11 |    TABLE ACCESS FULL                | BA_CC_BRN_MAST             |  3259 |   136K|       |    31   (0)| 00:11:04 |       |       |
|* 12 |    HASH JOIN                        |                            |   747K|    97M|    61M|   439K  (1)|999:59:59 |       |       |
|  13 |     PARTITION HASH ALL              |                            |   740K|    52M|       |   286K  (1)|999:59:59 |     1 |    64 |
|* 14 |      TABLE ACCESS FULL              | CH_ACCT_MAST               |   740K|    52M|       |   286K  (1)|999:59:59 |     1 |    64 |
|* 15 |     TABLE ACCESS FULL               | CH_ACCT_CBR_CODES          |  9154K|   541M|       |   117K  (1)|699:41:01 |       |       |
------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("COD_ACCT_NO"=:B1 AND "FLG_MNT_STATUS"='A' AND "COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_co
              de'),'0')))
   5 - access("COD_ACCT_NO"=:B1 AND "FLG_MNT_STATUS"='A' AND "COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_co
              de'),'0')))
   8 - access("COD_ACCT_NO"=:B1)
       filter("COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_code'),'0')))
  10 - access("COD_CC_BRN"="COD_CC_BRN")
  11 - filter("COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_code'),'0')))
  12 - access("COD_ACCT_NO"="COD_ACCT_NO")
  14 - filter(("COD_PROD"=164 OR "COD_PROD"=178 OR "COD_PROD"=181 OR "COD_PROD"=182 OR "COD_PROD"=187 OR "COD_PROD"=188 OR
              "COD_PROD"=200 OR "COD_PROD"=201 OR "COD_PROD"=202 OR "COD_PROD"=205 OR "COD_PROD"=207 OR "COD_PROD"=229 OR "COD_PROD"=231 OR
              "COD_PROD"=232 OR "COD_PROD"=235 OR "COD_PROD"=250 OR "COD_PROD"=253 OR "COD_PROD"=256 OR "COD_PROD"=274 OR "COD_PROD"=278 OR
              "COD_PROD"=279 OR "COD_PROD"=284 OR "COD_PROD"=285 OR "COD_PROD"=293 OR "COD_PROD"=298 OR "COD_PROD"=299 OR "COD_PROD"=770 OR
              "COD_PROD"=801 OR "COD_PROD"=802 OR "COD_PROD"=804 OR "COD_PROD"=805 OR "COD_PROD"=806 OR "COD_PROD"=809 OR "COD_PROD"=810 OR
              "COD_PROD"=811 OR "COD_PROD"=813 OR "COD_PROD"=814 OR "COD_PROD"=816 OR "COD_PROD"=817 OR "COD_PROD"=818 OR "COD_PROD"=819 OR
              "COD_PROD"=820 OR "COD_PROD"=825 OR "COD_PROD"=826 OR "COD_PROD"=827 OR "COD_PROD"=830 OR "COD_PROD"=831 OR "COD_PROD"=832 OR
              "COD_PROD"=833 OR "COD_PROD"=835 OR "COD_PROD"=837 OR "COD_PROD"=838 OR "COD_PROD"=843 OR "COD_PROD"=844 OR "COD_PROD"=845 OR
              "COD_PROD"=846 OR "COD_PROD"=847 OR "COD_PROD"=848 OR "COD_PROD"=853 OR "COD_PROD"=862 OR "COD_PROD"=863 OR "COD_PROD"=864 OR
              "COD_PROD"=866 OR "COD_PROD"=867 OR "COD_PROD"=868 OR "COD_PROD"=871 OR "COD_PROD"=881 OR "COD_PROD"=893 OR "COD_PROD"=894 OR
              "COD_PROD"=895 OR "COD_PROD"=897) AND "FLG_MNT_STATUS"='A' AND "COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_
              code'),'0')))
  15 - filter("FLG_MNT_STATUS"='A' AND "COD_ENTITY_VPD"=TO_NUMBER(NVL(SYS_CONTEXT('CLIENTCONTEXT','entity_code'),'0')))

Considering each table contains over 100 columns I am limited while uploading the entire table definition. however please find the below details for the columns accessed in the where clause. Hope this helps.

Columns     Type    Nullable
cod_acct_no CHAR(16)    N
FLG_MNT_STATUS  CHAR(1)     N 
cod_23          VARCHAR2(360)   Y
cod_cc_brn  NUMBER(5)   N
cod_prod    NUMBER          N
Was it helpful?

Solution

I Hope this can bring the cost down.

select 
distinct rtrim(a.cod_acct_no)||'|'||
a.cod_prod||'|'||
to_char(a.dat_acct_open,'Mon DD YYYY HH:MMAM')||'|'||
a.cod_acct_title||'|'||
a.cod_acct_stat||'|'||
ltrim(to_char(a.amt_od_limit,'99999999999999999990.999999'))||'|'||
ltrim(to_char(a.bal_book,'99999999999999999990.999999'))||'|'||
a.flg_idd_auth||'|'||
a.flg_mnt_status||'|'||
rtrim(c.cod_acct_no)||'|'||
c.cod_10||'|'||
d.nam_branch||'|'||
d.nam_cc_city||'|'||
d.nam_cc_state||'|'||
c.cod_1||'|'||
c.cod_14||'|'||
num_14||'|'||
a.cod_cust||'|'||
c.cod_last_mnt_chkrid||'|'||
c.dat_last_mnt||'|'||
c.ctr_updat_srlno||'|'||       
c.cod_20||'|'||            
c.num_16||'|'||
c.cod_14||'|'||                
c.num_10  ||'|'||
a.flg_classif_reqd||'|'||

g.cod_classif_plan_id||'|'||g.cod_classif_plan_id
 ||'|'||
 e.dat_cam_expiry ||'|'||
c.cod_23||'|'||
lpad(trim(a.cod_cc_brn),4,0)||'|'||
(select min( o.dat_eff)  from ch_acct_od_hist o where a.cod_acct_no=o.cod_acct_no )
from    
ch_acct_mast a
JOIN ch_acct_cbr_codes c
ON a.flg_mnt_status ='A'
 and c.flg_mnt_status ='A'
 and a.cod_acct_no= c.cod_acct_no(+)
JOIN    ba_cc_brn_mast d
a.cod_cc_brn=d.cod_cc_brn 
JOIN ac_acct_preferences g 
ON a.cod_acct_no=g.cod_acct_no AND g.FLG_MNT_STATUS = 'A'
INNER JOIN flexprod_host.AC_ACCT_PLAN_CRITERIA e
ON a.cod_acct_no=e.cod_acct_no   and e.FLG_MNT_STATUS ='A'

WHERE a.cod_prod in (
299,200,804,863,202,256,814,232,182,844,279,830,802,833,864,
813,862,178,205,801,235,897,231,187,229,847,164,868,805,207,
250,837,274,253,831,893,201,809,846,819,820,845,811,843,285,
894,284,817,832,278,818,810,181,826,867,825,848,871,866,895,
770,806,827,835,838,881,853,188,816,293,298)

OTHER TIPS

1. Don't fear full table scans. If a large percent of the rows in a table are being accessed it is more efficient to use a hash join/full table scan than a nested loop/index scan.

2. Fix statistics and re-analyze objects. 999 hours to read a table? That's probably an optimizer bug, have a dba look at select * from sys.aux_stats$; for some ridiculous values. The time isn't very useful, but if one of your forecasted values is so significantly off then you need to check all of them. You should probably re-gather stats on all the relevant tables. Use default settings unless there is a good reason. For example, exec dbms_stats.gather_table_stats('your_schema_name','CH_ACCT_MAST');.

3. Look at cardinalities. Are the Rows estimates in the ballpark? They'll almost never be perfect, but if they are off by more than an order of magnitude or two it can cause problems. Look for the first significant difference and try to correct it.

4. Code change. @Santhosh had a good idea to re-write using ANSI joins and manually unnest a subquery. Although I think you should try to unnest the other subquery instead. Oracle can automatically unnest subqueries, but not if subqueries "contain aggregate functions".

5. Disable VPD Looks like this query is being transformed. Make sure you understand exactly what it's doing and why. You may want to disable VPD temporarily, for yourself, while you debug this problem.

6. Parallelism. Since some of these tables are large, you may want to add a parallel hint. But be careful, it is easy to use up a lot of resources. Try to get the plan right before you do this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top