views:

1156

answers:

3

I have a partitioned table that belongs to tablespace report. I want to move it to tablespace record instead.

One possibility is to drop the table and recreate it in the new tablespace, but that is not an option for me, since there is data in the table that needs to survive the move.

I started by checking that the partitions actually belong to tablespace report with:

SELECT * FROM user_tab_partitions WHERE table_name = 'REQUESTLOG';

Then I just tried:

ALTER TABLE requestLog MOVE TABLESPACE record;

But that gives me error ORA-145111 “cannot perform operation on a partitioned object”.

Then I found out that I can move individual partitions using:

ALTER TABLE requestLog MOVE PARTITION "2009-12-29" TABLESPACE report;

But since there are 60 partitions of the table (based on date), and because I may have to do this for several systems, I would like to loop over all the partition names, moving each to the new tablespace. I tried that, but couldn’t quite get the SQL to work.

Even if I move all the existing partitions to the new tablespace, there is still a problem when creating new partitions. The new partitions are still created in the old tablespace report. How do I change so that new partitions are created in the new tablespace record?

A: 

If this is an option, the easiest way could be to rename the table (ALTER TABLE requestLog RENAME TO requestLogTmp;), create the same table with all indexes in the correct tablespace and copy the data from the old table:

INSERT INTO requestLog ( SELECT * FROM requestLogTmp )

When everything is up and running, you can drop the old table.

Peter Lang
That's not a good solution. It's more complicated than a pl/sql loop, It requires twice the space of the table, and it takes maybe an long time (eg if you have a table of a few 100gb)
zürigschnäzlets
When trying the INSERT statement, I get ORA-00936 ”missing expression” at the opening parenthesis.
Henrik Warne
Sorry, corrected the `INSERT`.
Peter Lang
+1  A: 

You can either do it with PL/SQL or generate the statements with sql. I decided to generate the alter table statements with simple SQL:

--set linesize
set lines 100

--This Query generates the alter table statements:
SELECT 'ALTER TABLE '
       ||table_name
       ||' MOVE PARTITION '
       ||partition_name
       ||' TABLESPACE REPORT;'
FROM   all_tab_partitions
WHERE  table_name = 'requestLog'; 

You can execute the output from the previous statement.

Every user has a default tablespace. New database objects are created in that default tablespace if nothing else is specified on creation/alteration

zürigschnäzlets
+1 for definitely providing the better solution.
Peter Lang
After adding qoutes around the partition_name, I can run the output. As for creating new partitions, how do I make the new partitions belong to the new tablespace (record)?
Henrik Warne
+2  A: 

You have to consider indexes that may be invalidated as well - to cover your question about resetting the default tablespaces in addition to this, I think this is the full process that you'll want to implement:

1) Move partitions (a PL/SQL loop as per zürigschnäzlets' answer)

These are procedures I use within an anonymous block wrapper that defines a_tname, a_destTS, vTname, and vTspName - they should give you the general idea:

procedure mvTabPart (a_tname in varchar2, a_destTS in varchar2) is
cursor pCur(vTname varchar2, vTspName varchar2) is
  select table_name, partition_name
  from user_tab_partitions
  where table_name = vTname
      and tablespace_name not like vTspName
  order by partition_position desc;
begin
for pRow in pCur(a_tname, a_destTS) loop
 sqlStmnt := 'alter table '||pRow.table_name||
             ' move partition '||pRow.partition_name||
             ' tablespace '||a_destTS;
execute immediate sqlStmnt;
end loop;
end mvTabPart;

2) Set table default partition tablespace so new partitions are created there:

    procedure setDefTabPart (a_tname in varchar2, a_destTS in varchar2) is
    cursor tCur(vTname varchar2) is
      select table_name
      from user_part_tables
      where table_name = vTname;
    begin
    for tRow in tCur(a_tname) loop
     sqlStmnt := 'alter table '||tRow.table_name||
                 ' modify default attributes '||
                 ' tablespace '||a_destTS;
    execute immediate sqlStmnt;
    end loop;
end setDefNdxPart;

3) Set index default partition tablespace so new index partitions (if any) are created where you want them:

procedure setDefNdxPart (a_tname in varchar2, a_destTS in varchar2) is
cursor iCur(vTname varchar2) is
  select index_name
  from user_part_indexes
  where index_name in (select index_name
             from user_indexes where table_name = vTname);
begin
for iRow in iCur(a_tname) loop
 sqlStmnt := 'alter index '||iRow.index_name||
             ' modify default attributes '||
             ' tablespace '||a_destTS;
execute immediate sqlStmnt;
end loop;

end setDefNdxPart;

4) rebuild any partitioned indexes that need rebuilding and are not in the desired tablespace:

procedure mvNdxPart (a_tname in varchar2, a_destTS in varchar2) is
cursor ndxCur(vTname varchar2, vTspName varchar2) is
select i.index_name index_name, ip.partition_name partition_name
  from user_ind_partitions ip, user_indexes i
  where i.index_name = ip.index_name
     and i.table_name = vTname
     and i.partitioned = 'YES'
     and (ip.tablespace_name not like vTspName or ip.status not like 'USABLE')
  order by index_name, partition_name ;
begin
for ndxRow in ndxCur(a_tname, a_destTS) loop
 sqlStmnt := 'alter index '||ndxRow.index_name||
             ' rebuild partition '||ndxRow.partition_name||
             ' tablespace '||a_destTS;
execute immediate sqlStmnt ;
end loop;
end mvNdxPart;

5) Rebuild any global indexes

procedure mvNdx (a_tname in varchar2, a_destTS in varchar2) is
cursor ndxCur(vTname varchar2, vTspName varchar2) is
  select index_name
  from user_indexes
  where table_name = vTname
       and partitioned = 'NO'
       and (tablespace_name not like vTspName or status like 'UNUSABLE')
  order by index_name ;
begin
for ndxRow in ndxCur(a_tname, a_destTS) loop
 sqlStmnt := 'alter index '||ndxRow.index_name||
             ' rebuild tablespace '||a_destTS;
execute immediate sqlStmnt ;
end loop;
end mvNdx;
dpbradley
Very detailed answer. 1+
zürigschnäzlets
I am using Oracle SQL Developer and I am not sure how to call the procedures you have given. However, I rearranged the first one (mvTabPart) a little bit and managed to execute it (got anonymous block completed). The only thing is that I had to add quotes around the partition name (‘move partition “’ and ‘” tablespace ‘), but then it worked like a charm.
Henrik Warne