Skip to Main Content
  • Questions
  • Alternative for greatest function to improve performance

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question.

Asked: September 10, 2018 - 9:13 am UTC

Last updated: September 10, 2018 - 4:06 pm UTC

Version: 4.1.3.20.78

Viewed 1000+ times

You Asked

Hi Tom,

I have a view, which has date_p,date_p_c. Now I have a ssas cube where it has many partitions based on date.I need to fetch the data based on latest date.


Monthly partition brings data from beginning of year to prior month.
Daily partition brings data from beginning of month to prior day.
hourly partition brings data fof that particular hour.

now monthhly partitions's query is like -

SELECT * FROM loc_view
WHERE  greatest(DATE,DATE_P_C) between TRUNC(SYSDATE, 'MONTH') and (TRUNC(SYSDATE, 'DD'))- INTERVAL '1' SECOND;


So when I use above query, the run time/processing time of cube is increasing.

Is there any other alternative for greatest function.I already tried OR but it is also not helping.

Thank you

and Chris said...

The greatest function means your query won't be able to use partition pruning. Or regular indexes on date or date_p_c. So you'll always scan every partition. And, as you've noticed, the query take longer and longer:

create table t (
  c1 int,
  c2 int
) partition by range ( c1 ) 
  interval ( 1 ) (
  partition p0 values less than ( 2 )
);

create index i1 on t ( c1 );
create index i2 on t ( c2 );

insert into t 
  with rws as (
    select level x , level + 1
    from   dual
    connect by level <= 10
  )
    select * from rws;
    
commit;
    
select * from t
where  greatest ( c1, c2 ) = 2;

C1   C2   
   1    2

select * 
from   table(dbms_xplan.display_cursor(null, null, 'BASIC LAST +PARTITION'));

PLAN_TABLE_OUTPUT                                      
EXPLAINED SQL STATEMENT:                               
------------------------                               
select * from t where  greatest ( c1, c2 ) = 2         
                                                       
Plan hash value: 4094519774                            
                                                       
----------------------------------------------------   
| Id  | Operation           | Name | Pstart| Pstop |   
----------------------------------------------------   
|   0 | SELECT STATEMENT    |      |       |       |   
|   1 |  PARTITION RANGE ALL|      |     1 |1048575|   
|   2 |   TABLE ACCESS FULL | T    |     1 |1048575|   
----------------------------------------------------


One way around this is to create a function-based index on greatest ( c1, c2 ). This allows the optimizer to use this index:

create index i on t ( greatest ( c1, c2 ) );

select * from t
where  greatest ( c1, c2 ) = 2;

C1   C2   
   1    2 

select * 
from   table(dbms_xplan.display_cursor(null, null, 'BASIC LAST +PARTITION'));

PLAN_TABLE_OUTPUT                                                             
EXPLAINED SQL STATEMENT:                                                      
------------------------                                                      
select * from t where  greatest ( c1, c2 ) = 2                                
                                                                              
Plan hash value: 4075433511                                                   
                                                                              
---------------------------------------------------------------------------   
| Id  | Operation                                  | Name | Pstart| Pstop |   
---------------------------------------------------------------------------   
|   0 | SELECT STATEMENT                           |      |       |       |   
|   1 |  TABLE ACCESS BY GLOBAL INDEX ROWID BATCHED| T    | ROWID | ROWID |   
|   2 |   INDEX RANGE SCAN                         | I    |       |       |   
--------------------------------------------------------------------------- 

Rating

  (3 ratings)

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

Comments

what if most data exists in a partition.

Rajeshwaran, Jeyabal, September 10, 2018 - 12:53 pm UTC

Let's say i got two partitions, where each partition has huge data sets, is it still possible to have partition pruning based on the outcome of the "greatest" function? Dont see that with the below test case.

kindly help us on this.

demo@ORA12C> create table t
  2  partition by range(c1)
  3  interval (1)
  4  ( partition p1 values less than(2) )
  5  as
  6  select 2 c1, 1 c2, a.*
  7  from all_objects a;

Table created.

demo@ORA12C> insert into t select 3 c1, 2 c2, a.*
  2  from all_objects a;

72354 rows created.

demo@ORA12C> commit;

Commit complete.

demo@ORA12C> create index t_idx on t( greatest(c1,c2) ) nologging;

Index created.

demo@ORA12C> exec dbms_stats.gather_table_stats(user,'T');

PL/SQL procedure successfully completed.

demo@ORA12C> set autotrace traceonly explain
demo@ORA12C> select *
  2  from t
  3  where greatest(c1,c2) = 2 ;

Execution Plan
----------------------------------------------------------
Plan hash value: 3557914527

--------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      | 72353 |     9M|   966   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE ALL|      | 72353 |     9M|   966   (2)| 00:00:01 |     1 |1048575|
|*  2 |   TABLE ACCESS FULL | T    | 72353 |     9M|   966   (2)| 00:00:01 |     1 |1048575|
--------------------------------------------------------------------------------------------

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

   2 - filter(GREATEST("C1","C2")=2)

demo@ORA12C> set autotrace off
demo@ORA12C>
demo@ORA12C> @script.sql

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
SQL_ID  64whn6nh4wm1b, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ * from t where greatest(c1,c2) = 2

Plan hash value: 3557914527

------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | Pstart| Pstop | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |      1 |        |       |       |  72352 |00:00:00.03 |    3500 |
|   1 |  PARTITION RANGE ALL|      |      1 |  72353 |     1 |1048575|  72352 |00:00:00.03 |    3500 |
|*  2 |   TABLE ACCESS FULL | T    |      3 |  72353 |     1 |1048575|  72352 |00:00:00.03 |    3500 |
------------------------------------------------------------------------------------------------------

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

   2 - filter(GREATEST("C1","C2")=2)


19 rows selected.

demo@ORA12C>

Chris Saxon
September 10, 2018 - 4:05 pm UTC

No. You can't prune if there's a function on the partition key:

There are several cases when the optimizer cannot perform pruning. One common reasons is when an operator is used on top of a partitioning column. This could be an explicit operator (for example, a function) or even an implicit operator introduced by Oracle as part of the necessary data type conversion for executing the statement

https://docs.oracle.com/database/121/VLDBG/GUID-C3CEB8F8-064E-45C5-97EE-E3BF1D00D517.htm

Reply to answer.

Sania, September 10, 2018 - 1:37 pm UTC

Hi Tom,

I am unable to find where I can reply you, so replying under review. I have the cdate,cdate_p_c columns in a view. And I am unable to create an index on view. It says -SQL Error: ORA-01702: a view is not appropriate here.

Please suggest.

Thank you.
Chris Saxon
September 10, 2018 - 4:06 pm UTC

What is your create index statement? What is the create table for the table you're creating this on?

Modify where clause ?

lh, March 09, 2021 - 9:09 am UTC

While searching another case I accidentally ran into this.

You might benefit from writing this query as:
SELECT * FROM loc_view
WHERE  
greatest(DATE,DATE_P_C) between TRUNC(SYSDATE, 'MONTH') and (TRUNC(SYSDATE, 'DD'))- INTERVAL '1' SECOND
and 
(
DATE between TRUNC(SYSDATE, 'MONTH') and (TRUNC(SYSDATE, 'DD'))- INTERVAL '1' SECOND
or
DATE_P_C between TRUNC(SYSDATE, 'MONTH') and (TRUNC(SYSDATE, 'DD'))- INTERVAL '1' SECOND
)

More to Explore

Administration

Need more information on Administration? Check out the Administrators guide for the Oracle Database