Skip to Main Content
  • Questions
  • Fetch Table partition name during runtime to execute any command

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Raghav.

Asked: June 09, 2016 - 6:30 am UTC

Last updated: June 10, 2016 - 3:16 pm UTC

Version: 10.2.0.4

Viewed 10K+ times! This question is

You Asked

Hi,

Is it possible to fetch the partition number on runtime from user_tab_partitions and execute the given SQL on that particular partitions only to avoid FULL TABLE SCAN.

My problem:
In my production system, We have a table who has around 8000 rolling Partitions( they are dynamically created and deleted acc to date) which are partitioned by TIMESTAMP interval@1 hr. A full table-scan takes around 9hrs to complete for simple SELECT command.

Table command looks as below:

CREATE TABLE SAMPLE_TAB_TEST
( ID NUMBER,
NAME VARCHAR(50);
START_TIME TIMESTAMP;
END_TIME TIMESTAMP;
)
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
STORAGE(BUFFER_POOL DEFAULT)
TABLESPACE "DATA"
PARTITION BY RANGE ("START_TIME")
(PARTITION "PART_201606080000_201606080010" VALUES LESS THAN (TIMESTAMP' 2016-06-08 00:10:00'),
PARTITION "PART_201606080010_201606080020" VALUES LESS THAN (TIMESTAMP' 2016-06-08 00:20:00'),
PARTITION "PART_201606080020_201606080030" VALUES LESS THAN (TIMESTAMP' 2016-06-08 00:30:00'))
TABLESPACE "DATA" NOCOMPRESS;

I have the idea that we can implement this like but not able to script it:

1) Fetch PARTITION_NAME.USER_TAB_PARTITIONS by matching the input timestamp with the HIGH_VALUE.USER_TAB_PARTITIONS.
2) Use this PARTITION_NAME into SELECT QUERY and minimize the execution time.
Something like : select * from SAMPLE_TAB_TEST PARTITION ( PARTITION_NAME ) where START_TIME BETWEEN to_date('2016-06-01 00:00:00','YYYY-MM_DD HH24:MI:SS') and to_date('2016-06-08 00:00:00','YYYY-MM_DD HH24:MI:SS')

If anyone have a solution this problem then it would be great and really helpful for me.

Thanks in Advance.

and Chris said...

You can find the partition boundaries in the user_tab_partitions.high_value. Unfortunately this is a long, so you have to do some SQL wizardry to make the values something usable:

create table sample_tab_test (    
  id   number, 
  name   varchar2(50 byte), 
  start_time   timestamp (6), 
  end_time   timestamp (6)
) partition by range (  start_time  )  (
 partition   part_201606080000_201606080010    values less than (timestamp' 2016-06-08 00:10:00') , 
 partition   part_201606080010_201606080020    values less than (timestamp' 2016-06-08 00:20:00') , 
 partition   part_201606080020_201606080030    values less than (timestamp' 2016-06-08 00:30:00') , 
 partition   part_201606080020_201606080040    values less than (timestamp' 2016-06-08 00:40:00')
);

insert into sample_tab_test
  select rownum, 'x', 
         timestamp' 2016-06-08 00:00:00'+numtodsinterval(rownum, 'minute'),
         timestamp' 2016-06-08 00:00:00'+numtodsinterval(rownum+1, 'minute')
  from   dual connect by level < 40;

with high_vals as
  (select dbms_xmlgen.getxmltype('
select p.high_value,
       p.partition_name
from   user_tab_partitions p
where  p.table_name = ''SAMPLE_TAB_TEST''') 
          as xml
   from   dual
), dts as (
  select partition_name, 
         to_date(substr(high_value, 12, 19), 'yyyy-mm-dd hh24:mi:ss') st_dt,
         lead(to_date(substr(high_value, 12, 19), 'yyyy-mm-dd hh24:mi:ss'))
           over (order by high_value) en_dt
  from   high_vals p,
         xmltable('/ROWSET/ROW'
          passing p.xml
          columns partition_name varchar2(30) 
                    path '/ROW/PARTITION_NAME',
                  high_value varchar2(60) 
                    path '/ROW/HIGH_VALUE'
         )
)
  select * from dts
  where  timestamp'2016-06-08 00:15:00' between st_dt and nvl(en_dt, timestamp'2016-06-08 00:15:00');

PARTITION_NAME                 ST_DT                EN_DT              
------------------------------ -------------------- --------------------
PART_201606080000_201606080010 08-JUN-2016 00:10:00 08-JUN-2016 00:20:00


Adrian Billington discusses more methods at:

http://www.oracle-developer.net/display.php?id=430

Having said this, I'm confused as to why you want this. Your query already has the partition key (start_time) in it. So Oracle will do partition elimination based on your from/to dates:

select /*+ gather_plan_statistics */* from sample_tab_test 
where  start_time between to_date('2016-06-08 00:15:00','yyyy-mm_dd hh24:mi:ss') 
                  and to_date('2016-06-08 00:25:00','yyyy-mm_dd hh24:mi:ss');
         
select * 
from   table(dbms_xplan.display_cursor(null, null, 'BASIC +PARTITION'));

--------------------------------------------------------------------                                                                                            
| Id  | Operation                | Name            | Pstart| Pstop |                                                                                            
--------------------------------------------------------------------                                                                                            
|   0 | SELECT STATEMENT         |                 |       |       |                                                                                            
|   1 |  PARTITION RANGE ITERATOR|                 |     2 |     3 |                                                                                            
|   2 |   TABLE ACCESS FULL      | SAMPLE_TAB_TEST |     2 |     3 |                                                                                            
--------------------------------------------------------------------

Notice how Oracle only accesses partitions 2 & 3 (pstart/pstop columns).

If you want all the data in the start/end time range you pass, then you'll have to run the partition query multiple times to get it:

select * from sample_tab_test partition ( p1 ) 
where start_time between :st and :en;
select * from sample_tab_test partition ( p2 ) 
where start_time between :st and :en;
...

This is likely to be slower than letting Oracle do the partition elimination itself!

Rating

  (3 ratings)

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

Comments

Amazing review

Raghav Agrawal, June 10, 2016 - 10:31 am UTC

I intend to this because the end customers are not aware of partitioning concept and they seldom attempts to manually execute the SQL over END_TIME or other timestamp based columns.

And raise the problem with the databases frequently.
Chris Saxon
June 10, 2016 - 3:16 pm UTC

How will you decide which partition to use if the partitioning key isn't supplied?

If users can write ad-hod queries, can you update the app to ensure the partition key is always supplied?

Query

Ghassan, June 10, 2016 - 6:05 pm UTC

"My problem:
In my production system, We have a table who has around 8000 rolling Partitions( they are dynamically created and deleted acc to date) which are partitioned by TIMESTAMP interval@1 hr. A full table-scan takes around 9hrs to complete for simple SELECT command. "

Could provide this select command ?

Query

Ghassan, June 10, 2016 - 6:05 pm UTC

"My problem:
In my production system, We have a table who has around 8000 rolling Partitions( they are dynamically created and deleted acc to date) which are partitioned by TIMESTAMP interval@1 hr. A full table-scan takes around 9hrs to complete for simple SELECT command. "

Could provide this select command ?

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.