【问题标题】:How do you move a partitioned table from one tablespace to another in Oracle 11g?如何在 Oracle 11g 中将分区表从一个表空间移动到另一个表空间?
【发布时间】:2010-01-04 10:16:41
【问题描述】:

我有一个属于表空间的分区表报告。我想将它移到表空间 record 中。

一种可能性是删除表并在新表空间中重新创建它,但这对我来说不是一个选项,因为表中的数据需要在移动中保留下来。

我首先检查分区是否确实属于表空间报告:

SELECT * FROM user_tab_partitions WHERE table_name = 'REQUESTLOG';

然后我就试了一下:

ALTER TABLE requestLog MOVE TABLESPACE record;

但这给了我错误 ORA-145111 “无法对分区对象执行操作”。

然后我发现我可以使用以下方法移动单个分区:

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

但是由于表有 60 个分区(基于日期),而且我可能需要为多个系统执行此操作,所以我想遍历所有分区名称,将每个分区名称移动到新表空间。我试过了,但无法让 SQL 正常工作。

即使我将所有现有分区都移动到新表空间,创建新分区时仍然存在问题。新分区仍然在旧表空间report 中创建。如何更改以便在新表空间 record 中创建新分区?

【问题讨论】:

标签: sql oracle tablespace


【解决方案1】:

您还必须考虑可能无效的索引 - 除了解决您关于重置默认表空间的问题之外,我认为这是您想要实现的完整过程:

1) 移动分区(根据 zürigschnäzlets 的回答,PL/SQL 循环)

这些是我在定义 a_tname、a_destTS、vTname 和 vTspName 的匿名块包装器中使用的过程 - 它们应该为您提供总体思路:

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) 设置表默认分区表空间,以便在那里创建新分区:

    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) 设置索引默认分区表空间,以便在您想要的位置创建新的索引分区(如果有):

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) 重建任何需要重建且不在所需表空间中的分区索引:

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) 重建所有全局索引

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;

【讨论】:

  • 我正在使用 Oracle SQL Developer,但我不确定如何调用您提供的程序。但是,我稍微重新安排了第一个(mvTabPart)并设法执行它(完成了匿名块)。唯一的问题是我必须在分区名称周围添加引号(“移动分区”和“表空间”),但它就像一个魅力。
  • 您是否还需要处理范围分区表? ALTER TABLE 表名 SET STORE IN (表空间);
  • 您还可以在索引重建语句的末尾添加 PARALLEL 以减少消耗的时间,但代价是更高的 CPU 和 IO。
【解决方案2】:

您可以使用 PL/SQL 执行此操作,也可以使用 sql 生成语句。我决定用简单的 SQL 生成 alter table 语句:

--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'; 

您可以执行上一条语句的输出。

每个用户都有一个默认表空间。如果在创建/更改时未指定任何其他内容,则会在该默认表空间中创建新的数据库对象

【讨论】:

  • 在 partition_name 周围添加 qoutes 后,我可以运行输出。至于新建分区,如何让新分区属于新的表空间(记录)?
【解决方案3】:

在表空间中移动数据的最简单方法:

移动所有非分区表

SELECT 'ALTER TABLE '||OWNER|| '.'||TABLE_NAME||' MOVE TABLESPACE ARCHIVE;'  
FROM ALL_tables 
where owner = 'owner_name' 
and temporary != 'Y'
and partitioned != 'YES';

分区表

SELECT 'ALTER TABLE '|| TABLE_OWNER||'.'||TABLE_NAME||' MOVE PARTITION ' ||  PARTITION_NAME||  ' TABLESPACE ARCHIVE;'  FROM ALL_tab_partitions 
WHERE TABLE_OWNER = 'owner_name' 
AND table_NAME NOT LIKE 'BIN$%';

非分区索引

SELECT 'ALTER INDEX '|| OWNER||'.'||OBJECT_NAME ||' REBUILD TABLESPACE ARCHIVE ;' 
FROM ALL_OBJECTS 
WHERE OBJECT_TYPE ='INDEX'
AND OWNER = 'owner_name';

分区索引

SELECT  'ALTER INDEX '||I.INDEX_NAME||'REBUILD PARITION'|| S.PARTITION_NAME || ' TABLESPACE  ARCHIVE ' 
                   FROM  DBA_INDEXES I,    DBA_SEGMENTS S
                  WHERE  I.INDEX_NAME = S.SEGMENT_NAME
                    AND I.INDEX_TYPE IN ('NORMAL', 'BITMAP')
                    AND I.OWNER = 'owner_name'; 

【讨论】:

    【解决方案4】:
    --MOVING ALL TABLES FROM USER  
    BEGIN
      FOR i IN (
        SELECT * FROM ALL_tables where owner = :owner 
          and (tablespace_name is null or tablespace_name != :tbs)
          and temporary != 'Y'
          and partitioned != 'YES'
        ) LOOP
        EXECUTE IMMEDIATE 'ALTER TABLE '  || i.table_name || ' MOVE TABLESPACE ' || :tbs;
      END LOOP; 
    END;
    
    
    --MOVING ALL INDEX
    
     BEGIN
      FOR i IN (
        SELECT * FROM ALL_tab_partitions 
        WHERE table_owner = :owner and tablespace_name != :tbs
      ) LOOP
        EXECUTE IMMEDIATE 'ALTER TABLE ' 
          || i.table_name || ' MOVE PARTITION '
          || i.partition_name ||' TABLESPACE '|| :tbs;
      END LOOP;
    END;
    
    
    --MOVING ALL PARTATION TABLES FROM USER  
    
    BEGIN
      FOR i IN (
        SELECT * FROM ALL_tables where owner = :owner and partitioned = 'YES'
      ) LOOP
        EXECUTE IMMEDIATE 'ALTER TABLE '
          || i.table_name || ' MODIFY DEFAULT ATTRIBUTES TABLESPACE ' || :tbs;
      END LOOP;
    END;
    

    【讨论】:

      【解决方案5】:

      如果这是一个选项,最简单的方法可能是重命名表 (ALTER TABLE requestLog RENAME TO requestLogTmp;),在正确的表空间中创建包含所有索引的同一个表,然后从旧表中复制数据:

      INSERT INTO requestLog ( SELECT * FROM requestLogTmp )
      

      一切正常运行后,您可以删除旧表。

      【讨论】:

      • 这不是一个好的解决方案。它比 pl/sql 循环更复杂,它需要两倍的表空间,并且可能需要很长时间(例如,如果你有一个 100gb 的表)
      • 在尝试 INSERT 语句时,我在左括号中得到 ORA-00936 “missing expression”。
      【解决方案6】:
          <pre><code>PROCEDURE P_ALTER_TABLE_SPACE(
              A_TNAME         IN VARCHAR2,
              A_DESTTS        IN VARCHAR2,
              A_PATITION_TYPE 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;
      
            CURSOR PCURR(VTNAME VARCHAR2, VTSPNAME VARCHAR2)
            IS
              SELECT TABLE_NAME,
                SUBPARTITION_NAME
              FROM USER_TAB_SUBPARTITIONS
              WHERE TABLE_NAME = VTNAME
              AND TABLESPACE_NAME NOT LIKE VTSPNAME
              ORDER BY SUBPARTITION_POSITION DESC;
          BEGIN
            IF A_PATITION_TYPE = 'PARTITION' THEN
              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;
            ELSE
              FOR PROW IN PCURR(A_TNAME, A_DESTTS)
              LOOP
                SQLSTMNT := 'ALTER TABLE '||PROW.TABLE_NAME|| ' MOVE SUBPARTITION '||PROW.SUBPARTITION_NAME|| ' TABLESPACE '||A_DESTTS;
                EXECUTE IMMEDIATE SQLSTMNT;
              END LOOP;
            END IF;
          END P_ALTER_TABLE_SPACE;
          </code></pre>
      

      【讨论】:

        猜你喜欢
        • 2013-08-22
        • 2021-04-12
        • 1970-01-01
        • 2021-05-04
        • 2017-03-22
        • 2021-10-12
        • 2019-08-16
        • 2019-12-30
        • 1970-01-01
        相关资源
        最近更新 更多