Skip to Main Content
  • Questions
  • Index skip scan with high NDV leading column

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, AHMED.

Asked: March 06, 2019 - 3:07 pm UTC

Last updated: March 14, 2019 - 8:45 am UTC

Version: 11.2.0.3

Viewed 1000+ times

You Asked

Hello Tom,

I have the situation: A table tbl (220 Mio recs) the following query

select col_a, col_b, col_c
from tbl t
where 
  Mod(Dbms_Rowid.Rowid_Row_Number(t.rowid, 2) = 0 --pred 1
and col_d =<lit>; --pred 2


col_d is in an index t_idx (col_e, col_d)
col_d : num_distinct => 17 Histogram Frequency
col_e : num_distinct => ~ 49 Mios Histogram Heught Balanced

Cardinality (real) of the where clause above is some 47 Mios records.

Note that pred 1 is a condition generated by an ETL software to do parallel reading (2 threads), there is a second query with mod=1.

Lately the query is taking forever to run (more than 26 hours!!).

Looking in the execution plan I found an "INDEX SKIP SCAN".

my questions:
1/ Why skip scan ? I always thought the leading column must have low NDV for an index skip scan.

2/I know pred1 is bad with regards to data location contention, should we go for rowid range. Or remove simply pred 1 and parallelize the select.

3/ I ran explain plan on the query with only pred 2 (col_d second in the index) => I got full table scan which I find understandable; why the combination of pred 1 and pred 2 gives index skip scan ?

Thank you for your answer,


and Chris said...

1. What is plan? To help here we need to see it!

And are you saying

col_d = <lit>


returns 47 million rows? And that there are 49 million different values in col_d?

2. I don't really understand why you've got that predicate. What's its purpose?

3. Same as 1: show us the plan!

Make sure you show us the execution plan. This must have columns showing:

- Estimated number of rows
- Actual number of rows
- Buffer/consistent gets for each step of the plan
- Last starts
- Times

Please ensure you also include the access/filter predicates.

https://blogs.oracle.com/sql/how-to-create-an-execution-plan

Rating

  (10 ratings)

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

Comments

Figures and plan

Ahmed, March 08, 2019 - 7:33 am UTC

Hi Chris,

Sorry for the lack of details in my question. Truth is I was out of office when I could actually submit it (the submission was closed each time I checked). So I laid out the general elements I knew.

BTW, great article you have for the options to get exec plan. In my case for the PROD environment I cannot do the actions involving change or run. The query is generated by the ETL software, I am reluctant to run a version on SQL developer because it runs for 23h :( and the risk of the workload it generates. So I opted for now => SQL Developer SQL Monitor possibility.
Fortunately it was running this morning. If the info is not sufficient I can reproduce in out test environment.

I'll answer first your questions and put below the report.

1/
col_d: No I said the cardinality of the where clause (i.e pred1 AND pred2) col_d is like a category 'lit'
col_d = 'lit' => 93.1 Mio (~40% of the table of 240 Mio)
Divided by 2 => cardinality of the where clause

col_e: No 49.7 Mios is num_distinct of col_e (leading column in index)

2/ pred1 is a ETL generated clause to implement what they call 'Rowid round robin' it's a splitting technique of theirs. Two processes: each submitting 1 query mod = 0 (1st), mod = 1 (2nd)

3/ Here is the report from sqltune.report sorry for renaming
------------------------------
SELECT col_a, col_b, col_c  FROM tbl t WHERE ( (MOD(DBMS_ROWID.ROWID_ROW_NUMBER(t.ROWID), 2) = 0)) AND ( col_d ='literal')

Global Information
------------------------------
 Status              :  EXECUTING                    
 Instance ID         :  1                            
 Session             :  xxxx (2268:5397)         
 SQL ID              :  g0950a7amstyd                
 SQL Execution ID    :  16777216                     
 Execution Started   :  03/07/2019 07:44:25          
 First Refresh Time  :  03/07/2019 07:44:32          
 Last Refresh Time   :  03/08/2019 07:49:42          
 Duration            :  86718s                       
 Module/Action       :  xxxx/- 
 Service             :  yyyy                    
 Program             :  xxxxxxx   
 Fetch Calls         :  16610                        

Global Stats
========================================================================================
| Elapsed |   Cpu   |    IO    | Concurrency | PL/SQL  | Fetch | Buffer | Read | Read  |
| Time(s) | Time(s) | Waits(s) |  Waits(s)   | Time(s) | Calls |  Gets  | Reqs | Bytes |
========================================================================================
|   86556 |    2881 |    83674 |        1.20 |     467 | 16610 |    26M |  11M |  86GB |
========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=1057769835)
=============================================================================================================================================================================
| Id   |           Operation           |     Name      |  Rows   | Cost |   Time    | Start  | Execs |   Rows   | Read | Read  | Activity |         Activity Detail         |
|      |                               |               | (Estim) |      | Active(s) | Active |       | (Actual) | Reqs | Bytes |   (%)    |           (# samples)           |
=============================================================================================================================================================================
| -> 0 | SELECT STATEMENT              |               |         |      |     86713 |     +7 |     1 |      33M |      |       |     0.15 | Cpu (24)                        |
|      |                               |               |         |      |           |        |       |          |      |       |          | SQL*Net more data to client (2) |
| -> 1 |   TABLE ACCESS BY INDEX ROWID | TBL           |    927K |   2M |     86713 |     +7 |     1 |      33M |  11M |  81GB |    93.69 | log buffer space (2)            |
|      |                               |               |         |      |           |        |       |          |      |       |          | Cpu (279)                       |
|      |                               |               |         |      |           |        |       |          |      |       |          | resmgr:cpu quantum (1)          |
|      |                               |               |         |      |           |        |       |          |      |       |          | db file sequential read (15700) |
|      |                               |               |         |      |           |        |       |          |      |       |          | read by other session (21)      |
| -> 2 |    INDEX SKIP SCAN            | t_idx         |    371K |   2M |     86713 |     +7 |     1 |      33M | 625K |   5GB |     6.16 | Cpu (531)                       |
|      |                               |               |         |      |           |        |       |          |      |       |          | db file sequential read (426)   |
|      |                               |               |         |      |           |        |       |          |      |       |          | read by other session (95)      |
============================================================================================================================================================================="


if you need other info please ask

Thx,
Chris Saxon
March 08, 2019 - 2:42 pm UTC

Soo... the plan is estimating it'll return ~371,000 rows from the index...

But you're getting 33,000,000+. Two orders of magnitude more!

So this is the root cause of the problem.

Could you share a version of the plan that includes the predicate information (SQL Monitor doesn't report this sadly...)

You can get this with:

select * 
from   table(dbms_xplan.display_cursor(<sqlid>, null, 'LAST +PREDICATE'));


If you could share a plan without the rowid predicate for comparison that would help.

home on the range

Racer I., March 08, 2019 - 7:52 am UTC

Hi,

2. looks like what we call MOD-parallelizing and use fairly often (I personally like PIPELINED-parallelizing more but that is syntactically more complex). Start X parallel sqlplus sessions telling each its number Y (1...X) and X itself. Then you can use some MOD(ID, X) = Y - 1 to split the work disjunctively. You have to "gather" the parts and their results afterwards somehow, which is what the pipelined approach does for you in addition to splitting the workload equitably without a need for MOD.

The above ROWID-technique is probably a generic version (with X = 2 in this case) so you don't have to know (or even have) a column to split on.

Another alternative is to split it by ID-ranges (with PERCENTILE_CONT, say). AQ offers some ROWID-range-splitting but I don't know how that works.

Goal is to make the processing part execute in parallel. If there is none a simple parallel INSERT/UPDATE/MERGE with a SELECT is of course sufficient.

I wouldn't worry about that part. If you expect to process millions of rows I would simple put a FULL() and maybe some HASH()-hints in there. I would also use more than 2 MOD-processes. Sometimes it also pays to add a PARALLEL(2) hint in the select to force direct path loads and keep the SGA clean.

It is somewhat mysterious why the MOD part makes the plan switch from the FULL-scan to an index access (with millions of rows any index use is bad).

regards,
Chris Saxon
March 08, 2019 - 2:45 pm UTC

It is somewhat mysterious why the MOD part makes the plan switch from the FULL-scan to an index access (with millions of rows any index use is bad).

There's a massive underestimate in the number of rows the index will return. I'm not sure if this is caused by the rowid predicate. Or it's simply enough to bring an already-low estimate under the threshold for it costing less than a FTS.

Hopefully the OP will be able to come back with a plan including predicates & another without this rowid in so we can compare.

Answers and 10053 trace

Ahmed, March 11, 2019 - 11:25 am UTC

Hi,

I have some figures and explanations.

The cardinality of the table in the plan: 926K, computed like follows:
cardinality from histogram 92,6 Mios x 1%
(There is an unknown(=) for predicate pred_1 ) => 926K

I don't know how the Index SS cardinality (371k) is computed.

I ran a 10053 trace in the query (slightly modified: mod 5000 instead of mod 2).

Below are some of the infos I got from it (note the "No default cost defined for function DBMS_ROWID" => unknown).

I don't understand how skip scan is chosen ! It took the cost SS io as number of leaf blocks !

***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: TBL  Alias: T
    #Rows: 240396618  #Blks:  11791564  AvgRowLen:  523.00  ChainCnt:  0.00
    
Index Stats::
  Index: t_idx  Col#: 3 4
    LVLS: 3  <b>#LB: 2017220</b>  #DK: 138594685  LB/K: 1.00  DB/K: 1.00  CLUF: 229689474.00

Access path analysis for TBL
***************************************
SINGLE TABLE ACCESS PATH
  Single Table Cardinality Estimation for TBL[TBL]
  Column (#4):
    NewDensity:0.000028, OldDensity:0.000000 BktCnt:18054, PopBktCnt:18053, PopValCnt:16, NDV:17
  Column (#4): COL_D(
    AvgLen: 57 NDV: 17 Nulls: 0 Density: 0.000028
    Histogram: Freq  #Bkts: 17  UncompBkts: 18054  EndPtVals: 17

  No statistics type defined for function DBMS_ROWID
  No default cost defined for function DBMS_ROWID
  Table: TBL  Alias: T
    Card: Original: 240396618.000000  Rounded: 926753  Computed: 926753.33  Non Adjusted: 926753.33
  Access Path: TableScan
    Cost:  3222653.15  Resp: 3222653.15  Degree: 0
      Cost_io: 3193550.00  Cost_cpu: 496510129396
      Resp_io: 3193550.00  Resp_cpu: 496510129396
kkofmx: index filter:MOD("SYS"."DBMS_ROWID"."ROWID_ROW_NUMBER"(ROWIDTOCHAR("TBL".ROWID)),5000)=0
kkofmx: index filter:"TBL"."col_d"='lit'

  Access Path: index (skip-scan)
    SS sel: 0.001542  ANDV (#skips): 49704960.000000
    <b>SS io: 2017220.000000</b> vs. table scan io: 3193550.000000
    Skip Scan chosen
  Access Path: index (SkipScan)
    Index: t_idx
    resc_io: 2371414.00  resc_cpu: 17328232356
    ix_sel: 0.001542  ix_sel_with_filters: 0.001542
    

============
Plan Table
============
----------------------------------------------------+-----------------------------------+
| Id  | Operation                    | Name         | Rows  | Bytes | Cost  | Time      |
----------------------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT             |              |       |       | 2317K |           |
| 1   |  TABLE ACCESS BY INDEX ROWID | TBL          |  905K |  135M | 2317K |  08:54:30 |
| 2   |   INDEX SKIP SCAN            | t_idx        |  362K |       | 1971K |  07:44:38 |
----------------------------------------------------+-----------------------------------+
Predicate Information:
----------------------
2 - access("COL_D"='lit')
2 - filter(("COL_D"='lit' AND MOD("DBMS_ROWID"."ROWID_ROW_NUMBER"(ROWIDTOCHAR("TBL".ROWID)),5000)=0))

Content of other_xml column
===========================
  db_version     : 11.2.0.3
  parse_schema   : xxxx
  plan_hash      : 1057769835
  plan_hash_2    : 1420993929
  Outline Data:
  /*+
    BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
      DB_VERSION('11.2.0.3')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX_SS(@"SEL$1" "TBL"@"SEL$1" ("TBL"."COL_E" "TBL"."COL_D"))
    END_OUTLINE_DATA
  */


If you need another info from the 10053, let me know.

As for the plan without pred_1, here it is (explain): FTS, 92M card table => Spot ON.

-----------------------------------------------------------------------------------
| Id  | Operation         | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |               |    92M|    12G|  3209K  (1)| 10:41:53 |
|*  1 |  TABLE ACCESS FULL| TBL           |    92M|    12G|  3209K  (1)| 10:41:53 |
-----------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("COL_D"='lit')


Thank you

so (in)dense

Racer I., March 12, 2019 - 8:31 am UTC

Hi,

Main problem seems to be that the optimizer seems to evaluate only the index-acccess cost (index full scan basically), but not the table access by rowid cost of that path. Which should be higher, even with the underestimate of the rows (360k).

Also the index-rows-estimate (360k) seems to be :
select 240396618 * 0.001542 from dual
370691.584956
i.e. all rows (no nulls in that index) times a density (ix_sel?, why not 1/17th? or 0.000028?). Which is way off as previously noted.

Guess I finally have to read up on density...

regards,

a bucket (to be sick in)

Racer I., March 12, 2019 - 9:34 am UTC

Hi,

Ok. Understanding density ... that way lies madness ;)

Curious :
Histogram: Freq #Bkts: 17 UncompBkts: 18054 EndPtVals: 17
NewDensity:0.000028, OldDensity:0.000000 BktCnt:18054, PopBktCnt:18053, PopValCnt:16, NDV:17

https://grokbase.com/t/freelists.org/oracle-l/067v73kq8y/uncompbkts-what-is-it-reference-to-10053-trace

UncompBkts in an Oracle 10.2 10053 trace for a frequency histogram is (most likely) not equal to the number of rows of the table, but equal to the number of rows with a not null value of the column.

At the moment I would guess the stats are out of date and the 'lit' value isn't known to exist so it estimates

NewDensity = [(BktCnt – PopBktCnt) / BktCnt] / (NDV – PopValCnt)

select 0.5 * (1 / 18054) / 1 from dual
0.000028
(0.5 because unknown value, rest is "least frequent value")

But it then uses the 0.001542, which I can't explain.

Why "number of rows" in a BktCnt- (sounds like number of buckets?)-parameter? With 17 NDV we should have 17 buckets, no?

regards,

skipping daintily and gathering dai.. ummh. blocks

Racer I., March 12, 2019 - 10:38 am UTC

Hi,

https://jonathanlewis.wordpress.com/2018/05/11/skip-scan-3/

> the change that appeared in 11.2.0.2 limited the I/O cost of an index skip scan to the total number of leaf blocks in the index.

Huh? What about the table access?

SS sel: 0.001542 ANDV (#skips): 49704960.000000
SS io: 2017220.000000 vs. table scan io: 3193550.000000
Skip Scan chosen

Ok. 2 mill. blocks in the index (leading column with high NDV -> index full scan, nothing to skip) plus 360.000 blocks from the table (worst case clustering) is still less than 3.2 mill. for the table.

I guess being pampered with storage nodes, multi_block_reads =128 and PARALLEL(16) I never think picking blocks from disk one by one can ever be a good idea even if it is technically fewer blocks.

Still the mysterious estimate of 0.001542 is unexplained.

LVLS: 3 #LB: 2017220 #DK: 138594685 LB/K: 1.00 DB/K: 1.00 CLUF: 229689474.00

The first indexed column appears to be almost unique. 140 mill. values for the index with 2. column ndv = 17. OP did say 50 mill. distinct in first column.

regards,

Costing Skip Scan - HOW ?

Ahmed, March 12, 2019 - 10:50 am UTC

Hi Racer,

Thx a bunch for you comments.

1/ Yes I agree it looks like an index full scan without the table access by rowid part !

2/ UncompBkts => Yes it's about rows. 18054 is the sample size.
I guess it's just some naming confusion.
Same goes for: BktCnt:18054, PopBktCnt:18053

I am at a loss as to how this Skip scan access path is costed !!

BR

PS: Side question How does it find the pupolar count? Here is the frequency distribution of the histograms:
3370
32
8
492
17
71
967
583
200
6960
90
48
29
4110
838
238
1


what went wrong with the costin ?

Ahmed, March 13, 2019 - 11:02 am UTC

Hi Racer,

Many thanks for the info and the link. Yes indeed it explains the IO cost we saw (nb leaf blocks).

I saw a mention of the pinned buffer blocks, that does not make it better to choose skip scan.

Furthermore as said earlier no table acces by index rowid costing at all!!

I looked at the index clustering factor this morning and it is HUGE:
Clustering factor: 229 689 474
(Table: Rows: 240 396 618, Blocks: 11 791 564)

I DO believe that pred 1 (introducing the "unknown") messed things up but HOW => mystery !
CBO chooses a FTS when pred 1 is removed.

I made another test: replace pred 1 by another one similar:
select col_a, col_b, col_c
from tbl 
where 
  MOD(col_pk,2) = 0 --pred 3
and col_d =<lit>; --pred 2

col_pk: technical unique column (number)

Note: pred 1 is a software generated (so don't ask why not pred3 instead of pred 1 ;) ).

I got the following plan (explain):
Plan hash value: 131303507
 
-----------------------------------------------------------------------------------
| Id  | Operation         | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |               |   926K|   130M|  3618K  (1)| 05:56:42 |
|*  1 |  TABLE ACCESS FULL| TBL           |   926K|   130M|  3618K  (1)| 05:56:42 |
-----------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(MOD("TBL"."COL_PK",2)=0 AND 
              "ROWID_SYSTEM"='MDMID')


It gave a FULL TABLE SCAN EVEN WITH THE UNKNOWN FACTOR
(Rows 926K is the estimate with the 1% of the unknown).

How to explain ?!!

Best Regards




Chris Saxon
March 13, 2019 - 1:56 pm UTC

Right, following some fiddling around and internal discussions, I believe the histogram and the mod predicate are red herrings. I can reproduce similar behaviour with the following test case. Note there's no histograms or wacky predicates:

create table t (
  c1 not null , c2 not null, c3, c4
) as
  select level, mod ( level, 17 ) c2,
         lpad ( 'x', 100, 'x' ) c3,
         mod(level,2) c4
  from   dual
  connect by level <= 500000;

create index i on t ( c1, c2 );

select count(c3) from t
where  c2 = 0;

exec dbms_stats.gather_table_stats ( user, 't',method_opt=>'for all columns size 1' ) ;

select column_name, num_distinct, num_buckets, histogram
from   user_tab_col_statistics
where  table_name = 'T'
order  by column_name;

COLUMN_NAME   NUM_DISTINCT   NUM_BUCKETS   HISTOGRAM   
C1                    500000             1 NONE        
C2                        17             1 NONE        
C3                         1             1 NONE        
C4                         2             1 NONE  

alter session set statistics_level = all;
set serveroutput off

select count(c3) from t
where  c2 = 0;

select *
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST +COST')); 

PLAN_TABLE_OUTPUT                                                                                                      
SQL_ID  a9yy44qsyfqjf, child number 1                                                                                  
-------------------------------------                                                                                  
select count(c3) from t where  c2 = 0                                                                                  
                                                                                                                       
Plan hash value: 1507512123                                                                                            
                                                                                                                       
--------------------------------------------------------------------------------------------------------------------   
| Id  | Operation                            | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |   
--------------------------------------------------------------------------------------------------------------------   
|   0 | SELECT STATEMENT                     |      |      1 |        |  1796 (100)|      1 |00:00:00.15 |    9386 |   
|   1 |  SORT AGGREGATE                      |      |      1 |      1 |            |      1 |00:00:00.15 |    9386 |   
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T    |      1 |  29412 |  1796   (1)|  29411 |00:00:00.14 |    9386 |   
|*  3 |    INDEX SKIP SCAN                   | I    |      1 |  29412 |  1320   (0)|  29411 |00:00:00.11 |    1321 |   
--------------------------------------------------------------------------------------------------------------------   
                                                                                                                       
Predicate Information (identified by operation id):                                                                    
---------------------------------------------------                                                                    
                                                                                                                       
   3 - access("C2"=0)                                                                                                  
       filter("C2"=0)

select /*+ full ( t ) */count(c3) from t
where  c2 = 0;

select *
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST +COST'));

PLAN_TABLE_OUTPUT                                                                                    
SQL_ID  6c8j5fzj5tgjd, child number 0                                                                
-------------------------------------                                                                
select /*+ full ( t ) */count(c3) from t where  c2 = 0                                               
                                                                                                     
Plan hash value: 2966233522                                                                          
                                                                                                     
--------------------------------------------------------------------------------------------------   
| Id  | Operation          | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |   
--------------------------------------------------------------------------------------------------   
|   0 | SELECT STATEMENT   |      |      1 |        |  2229 (100)|      1 |00:00:00.04 |    8074 |   
|   1 |  SORT AGGREGATE    |      |      1 |      1 |            |      1 |00:00:00.04 |    8074 |   
|*  2 |   TABLE ACCESS FULL| T    |      1 |  29412 |  2229   (1)|  29411 |00:00:00.04 |    8074 |   
--------------------------------------------------------------------------------------------------   
                                                                                                     
Predicate Information (identified by operation id):                                                  
---------------------------------------------------                                                  
                                                                                                     
   2 - filter("C2"=0)  


So it looks like in this case the optimizer assigning too low a cost to the skip scan. If you want us to address this, please raise this with support.

In the meantime as other predicates give a full scan, I suggest you find another way to carve up your load. The mod rowid trick looks a little shonky to me anyway!

now you seem now you don't

Racer I., March 14, 2019 - 6:40 am UTC

Hi,

Some more speculation. Although Oracle claims to not know (its own) function it probably does check where the parameters to it come from. So since the ROWID is found inside the index that maybe made it more alluring. Using MOD with the first index column would probably have the same effect and would fit the OPs findings, that another column not found in the index causes a FULL SCAN.

Would be helpful to see 10053-traces from the various versions and a non-skip-index access for comparison to see if the table access is ever considered. Maybe the generic rule is : index-scans are cheap AND will hit only "a few" rows so factoring table access costs in is not worth it.

As to how to address this : If the etl-tool can be influenced I had some suggestions in my first post (like a FULL-hint).
Assuming, however, that can't be changed (as is often the case) could you influence the db itself? Seems to be "yours" (and the tool is extracting from it instead of loading into it as is more usual)? Then you could add the HINT via SQL-profile. Alternatively I think the index could be replaced with one just on the leading column. In the worst case that adds 17 block accesses for each value to check for the other field value, which shouldn't be a problem. With the index out of the picture Oracle should go FULL-SCAN on its own.

Also where does the "ROWID_SYSTEM"='MDMID'-part come from in the OPs last test and what does it mean?

regards,
Chris Saxon
March 14, 2019 - 8:45 am UTC

Then you could add the HINT via SQL-profile. Alternatively I think the index could be replaced with one just on the leading column. In the worst case that adds 17 block accesses for each value to check for the other field value, which shouldn't be a problem. With the index out of the picture Oracle should go FULL-SCAN on its own.


Yep. Or create a baseline to lock the plan to a full-scan. In any case I think it's better to look into other solutions instead of getting caught up in why this happens.

Also where does the "ROWID_SYSTEM"='MDMID'-part come from


I don't know. We'd need a repeatable test case to look into this.

split methods

Ahmed, March 15, 2019 - 11:48 am UTC

Hi everybody,

Thx for the feedback. Some comments on the said above:

* Support: Yes I'll raise the issue with Oracle support as soon as I can get hold of the account and such...(can be tricky in in siloed enterprise :( )

* Predicate 'ROWID_SYSTEM"='MDMID': It's the real predicate :) My bad, sorry it missed my renaming. Plz read col_d='lit' (to be coherent with the rest).

* Splitting method and ETL : Actually the hint FULL() can do it, we can input it in the ETL.
Mod on leading column (col_e): not so good (column nearly unique but is not). Moreover, there is another better column in the table (unique). The issue with splitting predicate=>it is ETL generated and cannot be influenced. There are other methods (like range split) but still on ROWID.

We'll go for the hint as a workaround (simplest to implement); fortunately the other places where the spltting pattern is applied have some other predicates forcing the plans to be OK.

Thx and good day

More to Explore

Performance

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