Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, PAUL.

Asked: December 13, 2016 - 4:16 pm UTC

Last updated: June 28, 2019 - 8:41 am UTC

Version: 11g

Viewed 10K+ times! This question is

You Asked

Hi Guys,

I have this query to identify partitions to be moved to a new tablespace.

select distinct 'ALTER TABLE ' ||table_owner ||'.'||table_name|| ' MOVE PARTITION ' ||PARTITION_NAME|| ' TABLESPACE TBS_NAME UPDATE GLOBAL INDEXES;' from dba_tab_partitions
where table_owner= 'SCHEMA' and table_name not like 'BIN%' and table_name not like '%$%'
order by table_owner;

But it will move all partitions and I need to identify the latest partition for each table to not moved. is there something like max(partition_name) ?
How can I accomplish this

and Chris said...

Using the "partition for" syntax, you can instruct Oracle Database to only scan the partition which the value belongs to. For example:

create table t (
  x int
) partition by range (x) interval (10) (
  PARTITION p0 values less than (10)
);

insert into t 
  select rownum from dual
  connect by level < 100;
 
commit;

select * from t partition for (99);

X   
90  
91  
92  
93  
94  
95  
96  
97  
98  
99


So if you pass the maximum value for the partition key, you can then join to user_objects to get the partition name:

select max(uo.subobject_name)
from   t partition for (99), user_objects uo
where  dbms_rowid.rowid_object(t.rowid) = uo.data_object_id;

MAX(UO.SUBOBJECT_NAME)  
SYS_P188                


Unfortunately you can't pass a query or bind variable to this. So you'll have to use dynamic SQL:

declare
  l t.x%type;
  stmt varchar2(1000);
  part varchar2(30);
  
begin
  select max(x) into l from t;
  stmt := 'select max(uo.subobject_name)
from   t partition for (' || l || '), user_objects uo
where  dbms_rowid.rowid_object(t.rowid) = uo.data_object_id';
  
  execute immediate stmt into part;
  
  dbms_output.put_line(part);
end;
/

PL/SQL procedure successfully completed.
SYS_P188


Or you could inspect the high_value in user_tab_partitions. Unfortunately this is a long, making it tricky to work with. One way around this is to use DBMS_XMLGen to convert result to XML. Then extract out the appropriate parts:

with xml as (
  select  dbms_xmlgen.getxmltype('
    select partition_name, high_value from user_tab_partitions'
  ) x
  from dual
), vals as (
  select to_number(extractValue(column_value, 'ROW/HIGH_VALUE')) hv,
         extractValue(column_value, 'ROW/PARTITION_NAME') pn, 
         max(to_number(extractValue(column_value, 'ROW/HIGH_VALUE'))) over () mx
  from   xml, table(xmlsequence(extract(xml.x, '/ROWSET/ROW')))
)
  select *
  from   vals
  where  hv = mx;

HV   PN        MX   
100  SYS_P188  100 


For more about using DBMS_XMLGen to convert longs and other methods, read:

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

Rating

  (6 ratings)

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

Comments

PAUL VIDAL, December 13, 2016 - 6:25 pm UTC

So basically I can use something dinamic.

Like adding a loop to get all table names instead of the T only which is a single table name.
Chris Saxon
December 14, 2016 - 9:00 am UTC

What do you mean "a loop to get all table names"? Why do you need to do that? Surely you know which table your trying to find the max partition for?

partition_position

Laurent Schneider, December 14, 2016 - 9:33 am UTC

use partition_position to get the latest
select table_owner, table_name, max(partition_name) keep (dense_rank last order by partition_position) 
from dba_tab_partitions 
group by table_owner, table_name 
order by 1,2;

Chris Saxon
December 14, 2016 - 10:16 am UTC

Of course! Thanks for sharing.

Joseph Charpak, December 14, 2016 - 2:39 pm UTC

Chris in your initial response you used extractvalue and xmlsequence both of which are deprecated in Oracle 11g R2.

xmltable is the main replacement.

https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions251.htm#SQLRF06170 :

"Note:
The XMLSEQUENCE function is deprecated. It is still supported for backward compatibility. However, Oracle recommends that you use the XMLTABLE function instead. See XMLTABLE for more information."

https://docs.oracle.com/cloud/latest/db112/SQLRF/functions061.htm#SQLRF06173

"Note:
The EXTRACTVALUE function is deprecated. It is still supported for backward compatibility. However, Oracle recommends that you use the XMLTABLE function, or the XMLCAST and XMLQUERY functions instead. See XMLTABLE, XMLCAST, and XMLQUERY for more information."

So something like this:

with xml as (
  select  dbms_xmlgen.getxmltype('
     select partition_name, high_value from dba_tab_partitions'
  ) x
  from dual
), vals as (
  select "hv" hv,
         "pn" pn, 
         max("hv") over () mx
  from   xml,
          xmltable(
            '/ROWSET/ROW' 
            passing xml.x 
            columns 
               "hv" path 'HIGH_VALUE',
               "pn" path 'PARTITION_NAME' 
))
  select *
  from   vals
  where  hv = mx;



Chris Saxon
December 14, 2016 - 2:42 pm UTC

Good catch. Thanks for the updated version.

Get Data from Latest Partition

Jaromir D.B. Nemec, January 13, 2019 - 12:18 pm UTC

Some care should be probably taken if the use case is to get data from the lastest partition.

For interval partitioning the new partition is visible after the insert is started, but the data only after the commit. So for some (possible short) interval the latest partition is emtpy.

Is there a workaround to make the query on the latest partition safe in this matter?

Thanks,

Jaromir D.B. Nemec


Connor McDonald
January 14, 2019 - 12:39 am UTC

Once you have found your "potential" latest partition, you could do something like:

SQL> lock table t partition (p1) in exclusive mode wait 10;

Table(s) Locked.


to ensure there are no active transactions on the partition.


rank by partition position or creation date

Paul R, June 25, 2019 - 10:31 am UTC

If the "latest" partition is not the highest in rank by ranking user_tab_partitions.partition_position, we can join to user_objects and rank user_objects.created, avoiding the use of dynamic SQL altogether.
Chris Saxon
June 25, 2019 - 12:58 pm UTC

Good point.

Hash partitioning

A reader, June 28, 2019 - 4:54 am UTC

How can I get the partition name giving a key.
T is hash partitioned by id number.
How get partition name for some Id
Chris Saxon
June 28, 2019 - 8:41 am UTC

Like this:
create table t (
  c1 int
) partition by hash ( c1 ) 
  partitions 4;

insert into t values ( 1 );

select t.*, uo.subobject_name
from   t, user_objects uo
where  dbms_rowid.rowid_object(t.rowid) = uo.data_object_id
order  by 1;

C1    SUBOBJECT_NAME   
    1 SYS_P508   

More to Explore

Analytics

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