1. 程式人生 > >oracle 非分割槽錶轉分割槽表

oracle 非分割槽錶轉分割槽表

將普通錶轉換成分割槽表4種方法:

       1. Export/import method

       2. Insert with a subquery method

       3. Partition exchange method

       4. DBMS_REDEFINITION

具體參考:

       How to Partition a Non-partitioned Table [ID 1070693.6]

       邏輯匯出匯入這裡就不做說明,我們看看其他三種方法。

2.1  插入:Insert with a subquery method

這種方法就是使用

insert 來實現。當然在建立分割槽表的時候可以一起插入資料,也可以建立好後在insert 進去。這種方法採用DDL語句,不產生UNDO,只產生少量REDO,建表完成後資料已經在分佈到各個分割槽中。

SQL> select count(*) from dba;

  COUNT(*)

----------

   2713235

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

會話已更改。

SQL> select time_fee from dba where rownum<5;

TIME_FEE

-------------------

2011-02-17 19:29:09

2011-02-17 19:29:15

2011-02-17 19:29:18

2011-02-17 19:29:20

SQL>

2.1.1  Oracle 11gInterval

       在11g裡的Interval建立,這種方法對沒有寫全的分割槽會自動建立。 比如我這裡只寫了1月日期,如果插入的資料有其他月份的,會自動生成對應的分割槽。

/* Formatted on 2011/03/02 15:41:09 (QP5 v5.115.810.9015) */

CREATETABLE intervaldave

PARTITIONBYRANGE

(time_fee)

INTERVAL(NUMTOYMINTERVAL(1,'MONTH'))

(PARTITION part1

VALUESLESSTHAN(TO_DATE('01/12/2010','MM/DD/YYYY')))

AS

SELECTID, TIME_FEE FROMDAVE;

SQL> select table_name,partition_name from user_tab_partitions where table_name='INTERVALDAVE';

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

INTERVALDAVE                   PART1

INTERVALDAVE                   SYS_P24

INTERVALDAVE                   SYS_P25

INTERVALDAVE                   SYS_P26

INTERVALDAVE                   SYS_P33

INTERVALDAVE                   SYS_P27

INTERVALDAVE                   SYS_P28

2.1.2  

       在10g裡面,我需要寫全所有的分割槽。

sql> create table pdba (id, time) partition by range (time)

  2 (partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')),

  3    partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')),

  4    partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')),

  5    partition p4 values less than (maxvalue))

  6    as select id, time_fee from dba;

表已建立。

SQL> select table_name,partition_name from user_tab_partitions where table_name='PDBA';

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

PDBA                           P1

PDBA                           P2

PDBA                           P3

PDBA                           P4

sql> select count(*) from pdba partition (p1);

  count(*)

----------

   1718285

sql> select count(*) from pdba partition (p2);

  count(*)

----------

    183667

sql> select count(*) from pdba partition (p3);

  count(*)

----------

    188701

sql> select count(*) from pdba partition (p4);

  count(*)

----------

    622582

sql>

現在分割槽表已經建好了,但是表名不一樣,需要用rename對錶重新命名一下:

SQL> rename dba to dba_old;

表已重新命名。

SQL> rename pdba to dba;

表已重新命名。

SQL> select table_name,partition_name from user_tab_partitions where table_name='DBA';

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

DBA                            P1

DBA                            P2

DBA                            P3

DBA                            P4

2.2 . 交換分割槽:Partition exchange method

這種方法只是對資料字典中分割槽和表的定義進行了修改,沒有資料的修改或複製,效率最高。適用於包含大資料量的錶轉到分割槽表中的一個分割槽的操作。儘量在閒時進行操作。

交換分割槽的操作步驟如下:

1. 建立分割槽表,假設有2個分割槽,P1P2.

2. 建立表A存放P1規則的資料。

3. 建立表存放P2規則的資料。

4. 用表P1 分割槽交換。把表A的資料放到到P1分割槽

5. 用表p2 分割槽交換。把表B的資料存放到P2分割槽。

建立分割槽表:

sql> create table p_dba

  2  (id number,time date)

  3  partition by range(time)

  4  (

  5  partition p1 values less than (to_date('2010-09-1', 'yyyy-mm-dd')),

  6  partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd'))

  7  );

表已建立。

注意:我這裡只建立了2個分割槽,沒有建立存放其他資料的分割槽。

建立2個分別對應分割槽的基表:

SQL> CREATE TABLE dba_p1 as SELECT id,time_fee FROM dba_old WHERE  time_fee<TO_DATE('2010-09-1', 'YYYY-MM-DD');

表已建立。

SQL> CREATE TABLE dba_p2 as SELECT id,time_fee FROM dba_old WHERE  time_fee<TO_DATE('2010-11-1', 'YYYY-MM-DD') and time_fee>TO_DATE('2010-09-1', 'YYYY-MM-DD');

表已建立。

SQL> select count(*) from dba_p1;

  COUNT(*)

----------

   1536020

SQL> select count(*) from dba_p2;

  COUNT(*)

----------

    365932

SQL>

2個基表與2個分割槽進行交換:

SQL> alter table p_dba exchange partition p1 with table dba_p1;

表已更改。

SQL> alter table p_dba exchange partition p2 with table dba_p2;

表已更改。

查詢2個分割槽:

SQL> select count(*) from p_dba partition(p1);

  COUNT(*)

----------

   1536020

SQL> select count(*) from p_dba partition(p2);

  COUNT(*)

----------

    365932

注意:資料和之前的基表一致。

查詢原來的2個基表:

SQL> select count(*) from dba_p2;

  COUNT(*)

----------

         0

SQL> select count(*) from dba_p1;

  COUNT(*)

----------

         0

注意: 2個基表的資料變成成0

       在這裡我們看一個問題,一般情況下,我們在建立分割槽表的時候,都會有一個其他分割槽,用來存放不匹配分割槽規則的資料。 在這個例子中,我只建立了2個分割槽,沒有建立maxvalue分割槽。 現在我來插入一條不滿足規則的資料,看結果:

SQL> insert into p_dba values(999999,to_date('2012-12-29','yyyy-mm-dd'));

insert into p_dba values(999999,to_date('2012-12-29','yyyy-mm-dd'))

            *

第 1 行出現錯誤:

ORA-14400: 插入的分割槽關鍵字未對映到任何分割槽

SQL> insert into p_dba values(999999,to_date('2009-12-29','yyyy-mm-dd'));

已建立 1 行。

SQL> select * from p_dba where id=999999;

        ID TIME

---------- --------------

    999999 29-12月-09

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

會話已更改。

SQL> select * from p_dba where id=999999;

        ID TIME

---------- -------------------

    999999 2009-12-29 00:00:00

SQL>

通過這個測試可以清楚,如果插入的資料不滿足分割槽規則,會報ORA-14400錯誤。

2.3 . 使用線上重定義:DBMS_REDEFINITION

       線上重定義能保證資料的一致性,在大部分時間內,表都可以正常進行DML操作。只在切換的瞬間鎖表,具有很高的可用性。這種方法具有很強的靈活性,對各種不同的需要都能滿足。而且,可以在切換前進行相應的授權並建立各種約束,可以做到切換完成後不再需要任何額外的管理操作。

關於DBMS_REDEFINITION的介紹,參考官方連線:

關於用線上重定義建立分割槽表,參考:

How To Partition Existing Table Using DBMS_Redefinition [ID 472449.1]

這個功能只在9.2.0.4以後的版本才有,線上重定義表具有以下功能:

       (1)修改表的儲存引數;

       (2)將錶轉移到其他表空間;

       (3)增加並行查詢選項;

       (4)增加或刪除分割槽;

       (5)重建表以減少碎片;

       (6)將堆表改為索引組織表或相反的操作;

       (7)增加或刪除一個列。

使用線上重定義的一些限制條件:

(1) There must be enough space to hold two copies of the table.

(2) Primary key columns cannot be modified.

(3) Tables must have primary keys.

(4) Redefinition must be done within the same schema.

(5) New columns added cannot be made NOT NULL until after the redefinition operation.

(6) Tables cannot contain LONGs, BFILEs or User Defined Types.

(7) Clustered tables cannot be redefined.

(8) Tables in the SYS or SYSTEM schema cannot be redefined.

(9) Tables with materialized view logs or materialized views defined on them cannot be redefined.

(10) Horizontal sub setting of data cannot be performed during the redefinition.

在Oracle 10.2.0.4和11.1.0.7 版本下,線上重定義可能會遇到如下bug:

Bug 7007594 - ORA-600 [12261]

線上重定義的大致操作流程如下:

       (1)建立基礎表A,如果存在,就不需要操作。

       (2)建立臨時的分割槽表B。

       (3)開始重定義,將基表A的資料匯入臨時分割槽表B。

       (4)結束重定義,此時在DB的 Name Directory裡,已經將2個表進行了交換。即此時基表A成了分割槽表,我們建立的臨時分割槽表B 成了普通表。 此時我們可以刪除我們建立的臨時表B。它已經是普通表。

下面看一個示例:

1. 建立基本表和索引

sql> conn icd/icd;

已連線。

sql> create table unpar_table (

  2  id number(10) primary key,

  3  create_date date

  4  );

表已建立。

sql> insert into unpar_table select rownum, created from dba_objects;

已建立72288行。

sql> create index create_date_ind on unpar_table(create_date);

索引已建立。

sql> commit;

提交完成。

2. 收集表的統計資訊

sql> exec dbms_stats.gather_table_stats('icd', 'unpar_table', cascade => true);

pl/sql 過程已成功完成。

3. 建立臨時分割槽表

sql> create table  par_table (id number primary key, time date) partition by range (time)

  2  (partition p1 values less than (to_date('2004-7-1', 'yyyy-mm-dd')),

  3  partition p2 values less than (to_date('2005-1-1', 'yyyy-mm-dd')),

  4  partition p3 values less than (to_date('2005-7-1', 'yyyy-mm-dd')),

  5  partition p4 values less than (maxvalue));

表已建立。

4. 進行重定義操作

4.1 檢查重定義的合理性

sql> exec dbms_redefinition.can_redef_table('icd', 'unpar_table');

pl/sql 過程已成功完成。

4.2 如果4.1 沒有問題,開始重定義,這個過程可能要等一會。

這裡要注意:如果分割槽表和原表列名相同,可以用如下方式進行:

SQL> BEGIN

DBMS_REDEFINITION.start_redef_table(

uname => 'ICD', 

orig_table => 'unpar_table',

int_table => 'par_table');

END;

/

       如果分割槽表的列名和原表不一致,那麼在開始重定義的時候,需要重新指定對映關係:

SQL> EXEC DBMS_REDEFINITION.START_REDEF_TABLE(

'ICD',

'unpar_table',

'par_table',

'ID ID, create_date TIME', -- 在這裡指定新的對映關係

DBMS_REDEFINITION.CONS_USE_PK);

這一步操作結束後,資料就已經同步到這個臨時的分割槽表裡來了。

4.3 同步新表,這是可選的操作

SQL> BEGIN

  2  dbms_redefinition.sync_interim_table(

  3  uname => 'ICD',

  4  orig_table => 'unpar_table',

  5  int_table => 'par_table');

  6  END;

  7  /

PL/SQL 過程已成功完成。

4.4 建立索引,線上重定義只重定義資料,索引還需要單獨建立。

sql> create index create_date_ind2 on par_table(time);

索引已建立。

4.5 收集新表的統計資訊

sql> exec dbms_stats.gather_table_stats('icd', 'par_table', cascade => true);

pl/sql 過程已成功完成。

4.6 結束重定義

SQL> BEGIN

  2  dbms_redefinition.finish_redef_table(

  3  uname => 'ICD',

  4  orig_table => 'unpar_table',

  5  int_table => 'par_table');

  6  END;

  7  /

PL/SQL 過程已成功完成。

結束重定義的意義:

       基表unpar_table 和臨時分割槽表par_table 進行了交換。 此時臨時分割槽表par_table成了普通表,我們的基表unpar_table成了分割槽表。

       我們在重定義的時候,基表unpar_table是可以進行DML操作的。 只有在2個表進行切換的時候會有短暫的鎖表。

5. 刪除臨時表

SQL> DROP TABLE par_table;

表已刪除。

6. 索引重新命名

SQL> ALTER INDEX create_date_ind2 RENAME TO create_date_ind;

索引已更改。

7. 驗證

sql> select partitioned from user_tables where table_name = 'UNPAR_TABLE';

par

---

yes

sql> select partition_name from user_tab_partitions where table_name = 'UNPAR_TABLE';

partition_name

------------------------------

p1

p2

p3

p4

sql> select count(*) from unpar_table;

  count(*)

----------

     72288

sql> select count(*) from unpar_table partition (p4);

  count(*)

----------

     72288

sql>