一. 分區表理論知識
Oracle提供了分區技術以支持VLDB(Very Large DataBase)。分區表經過對分區列的判斷,把分區列不一樣的記錄,放到不一樣的分區中。分區徹底對應用透明。
Oracle的分區表能夠包括多個分區,每一個分區都是一個獨立的段(SEGMENT),能夠存放到不一樣的表空間中。查詢時能夠經過查詢表來訪問各個分區中的數據,也能夠經過在查詢時直接指定分區的方法來進行查詢。 sql
When to Partition a Table何時須要分區表,官網的2個建議以下: shell
- Tables greater than 2GB should always be considered for partitioning.
- Tables containing historical data, in which new data is added into the newest partition. A typical example is a historical table where only the current month's data is updatable and the other 11 months are read only.
在oracle 10g中最多支持:1024k-1個分區:
Tables can be partitioned into up to 1024K-1 separate partitions
聯機文檔上有關分區表和索引的說明:
Partitioned Tables and Indexes
http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/partconc.htm#sthref2604 session
分區提供如下優勢:
(1)因爲將數據分散到各個分區中,減小了數據損壞的可能性;
(2)能夠對單獨的分區進行備份和恢復;
(3)能夠將分區映射到不一樣的物理磁盤上,來分散IO;
(4)提升可管理性、可用性和性能。 oracle
Oracle 10g提供瞭如下幾種分區類型:
(1)範圍分區(range);
(2)哈希分區(hash);
(3)列表分區(list);
(4)範圍-哈希複合分區(range-hash);
(5)範圍-列表複合分區(range-list)。 app
Range分區:
Range分區是應用範圍比較廣的表分區方式,它是以列的值的範圍來作爲分區的劃分條件,將記錄存放到列值所在的range分區中。
如按照時間劃分,2010年1月的數據放到a分區,2月的數據放到b分區,在建立的時候,須要指定基於的列,以及分區的範圍值。
在按時間分區時,若是某些記錄暫沒法預測範圍,能夠建立maxvalue分區,全部不在指定範圍內的記錄都會被存儲到maxvalue所在分區中。
如: less
create table pdba (id number, time date) partition by range (time) ( partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')), partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')), partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')), partition p4 values less than (maxvalue) )
Hash分區:
對於那些沒法有效劃分範圍的表,可使用hash分區,這樣對於提升性能仍是會有必定的幫助。hash分區會將表中的數據平均分配到你指定的幾個分區中,列所在分區是依據分區列的hash值自動分配,所以你並不能控制也不知道哪條記錄會被放到哪一個分區中,hash分區也能夠支持多個依賴列。
如: ide
create table test ( transaction_id number primary key, item_id number(8) not null ) partition by hash(transaction_id) ( partition part_01 tablespace tablespace01, partition part_02 tablespace tablespace02, partition part_03 tablespace tablespace03 );
在這裏,咱們指定了每一個分區的表空間。 性能
List分區:
List分區也須要指定列的值,其分區值必須明確指定,該分區列只能有一個,不能像range或者hash分區那樣同時指定多個列作爲分區依賴列,但它的單個分區對應值能夠是多個。
在分區時必須肯定分區列可能存在的值,一旦插入的列值不在分區範圍內,則插入/更新就會失敗,所以一般建議使用list分區時,要建立一個default分區存儲那些不在指定範圍內的記錄,相似range分區中的maxvalue分區。
在根據某字段,如城市代碼分區時,能夠指定default,把非分區規則的數據,所有放到這個default分區。
如: 測試
create table custaddr ( id varchar2(15 byte) not null, areacode varchar2(4 byte) ) partition by list (areacode) ( partition t_list025 values ('025'), partition t_list372 values ('372') , partition t_list510 values ('510'), partition p_other values (default) )
組合分區:
若是某表按照某列分區以後,仍然較大,或者是一些其它的需求,還能夠經過分區內再建子分區的方式將分區再分區,即組合分區的方式。
組合分區呢在10g中有兩種:range-hash,range-list。注意順序,根分區只能是range分區,子分區能夠是hash分區或list分區。
如: 大數據
create table test ( transaction_id number primary key, transaction_date date ) partition by range(transaction_date) subpartition by hash(transaction_id) subpartitions 3 store in (tablespace01,tablespace02,tablespace03) ( partition part_01 values less than(to_date(’2009-01-01’,’yyyy-mm-dd’)), partition part_02 values less than(to_date(’2010-01-01’,’yyyy-mm-dd’)), partition part_03 values less than(maxvalue) ); create table emp_sub_template (deptno number, empname varchar(32), grade number) partition by range(deptno) subpartition by hash(empname) subpartition template (subpartition a tablespace ts1, subpartition b tablespace ts2, subpartition c tablespace ts3, subpartition d tablespace ts4 ) (partition p1 values less than (1000), partition p2 values less than (2000), partition p3 values less than (maxvalue) ); create table quarterly_regional_sales (deptno number, item_no varchar2(20), txn_date date, txn_amount number, state varchar2(2)) tablespace ts4 partition by range (txn_date) subpartition by list (state) (partition q1_1999 values less than (to_date('1-apr-1999','dd-mon-yyyy')) (subpartition q1_1999_northwest values ('or', 'wa'), subpartition q1_1999_southwest values ('az', 'ut', 'nm'), subpartition q1_1999_northeast values ('ny', 'vm', 'nj'), subpartition q1_1999_southeast values ('fl', 'ga'), subpartition q1_1999_northcentral values ('sd', 'wi'), subpartition q1_1999_southcentral values ('ok', 'tx') ), partition q2_1999 values less than ( to_date('1-jul-1999','dd-mon-yyyy')) (subpartition q2_1999_northwest values ('or', 'wa'), subpartition q2_1999_southwest values ('az', 'ut', 'nm'), subpartition q2_1999_northeast values ('ny', 'vm', 'nj'), subpartition q2_1999_southeast values ('fl', 'ga'), subpartition q2_1999_northcentral values ('sd', 'wi'), subpartition q2_1999_southcentral values ('ok', 'tx') ), partition q3_1999 values less than (to_date('1-oct-1999','dd-mon-yyyy')) (subpartition q3_1999_northwest values ('or', 'wa'), subpartition q3_1999_southwest values ('az', 'ut', 'nm'), subpartition q3_1999_northeast values ('ny', 'vm', 'nj'), subpartition q3_1999_southeast values ('fl', 'ga'), subpartition q3_1999_northcentral values ('sd', 'wi'), subpartition q3_1999_southcentral values ('ok', 'tx') ), partition q4_1999 values less than ( to_date('1-jan-2000','dd-mon-yyyy')) (subpartition q4_1999_northwest values ('or', 'wa'), subpartition q4_1999_southwest values ('az', 'ut', 'nm'), subpartition q4_1999_northeast values ('ny', 'vm', 'nj'), subpartition q4_1999_southeast values ('fl', 'ga'), subpartition q4_1999_northcentral values ('sd', 'wi'), subpartition q4_1999_southcentral values ('ok', 'tx') ) );
在Oracle 11g中,組合分區功能這塊有所加強,又增長了range-range,list-range,
list-list,list-hash,而且 11g裏面還支持Interval分區和虛擬列分區。
這塊能夠參考Blog:
Oracle 11g 新特性簡介
http://blog.csdn.net/tianlesoftware/archive/2010/01/06/5134819.aspx
分區表 之 Interval分區 和 虛擬列 按星期分區表
http://blog.csdn.net/tianlesoftware/archive/2010/06/10/5662337.aspx
二. 普通錶轉分區表方法
將普通錶轉換成分區表有4種方法:
- Export/import method
- Insert with a subquery method
- Partition exchange method
- DBMS_REDEFINITION
具體參考:
How to Partition a Non-partitioned Table [ID 1070693.6]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218704.aspx
邏輯導出導入這裏就不作說明,咱們看看其餘三種方法。
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 11g的Interval
在11g裏的Interval建立,這種方法對沒有寫全的分區會自動建立。 好比我這裏只寫了1月日期,若是插入的數據有其餘月份的,會自動生成對應的分區。
/* Formatted on 2011/03/02 15:41:09 (QP5 v5.115.810.9015) */ CREATE TABLE intervaldave PARTITION BY RANGE (time_fee) INTERVAL ( NUMTOYMINTERVAL (1, 'MONTH') ) (PARTITION part1 VALUES LESS THAN (TO_DATE ('01/12/2010', 'MM/DD/YYYY'))) AS SELECT ID, TIME_FEE FROM DAVE;
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 Oracle 10g 版本
在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
這種方法只是對數據字典中分區和表的定義進行了修改,沒有數據的修改或複製,效率最高。適用於包含大數據量的錶轉到分區表中的一個分區的操做。儘可能在閒時進行操做。
交換分區的操做步驟以下:
- 建立分區表,假設有2個分區,P1,P2.
- 建立表A存放P1規則的數據。
- 建立表B 存放P2規則的數據。
- 用表A 和P1 分區交換。 把表A的數據放到到P1分區
- 用表B 和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的介紹,參考官方鏈接:
http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_redefi.htm#CBBFDJBC
關於用在線重定義建立分區表,參考:
How To Partition Existing Table Using DBMS_Redefinition [ID 472449.1]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218693.aspx
這個功能只在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]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218681.aspx
在線重定義的大體操做流程以下:
(1)建立基礎表A,若是存在,就不須要操做。
(2)建立臨時的分區表B。
(3)開始重定義,將基表A的數據導入臨時分區表B。
(4)結束重定義,此時在DB的 Name Directory裏,已經將2個表進行了交換。即此時基表A成了分區表,咱們建立的臨時分區表B 成了普通表。 此時咱們能夠刪除咱們建立的臨時表B。它已是普通表。
下面看一個示例:
-
建立基本表和索引
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;
提交完成。
-
收集表的統計信息
sql> exec dbms_stats.gather_table_stats('icd', 'unpar_table', cascade => true);
pl/sql 過程已成功完成。
-
建立臨時分區表
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.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個表進行切換的時候會有短暫的鎖表。
- 刪除臨時表
SQL> DROP TABLE par_table;
表已刪除。
- 索引重命名
SQL> ALTER INDEX create_date_ind2 RENAME TO create_date_ind;
索引已更改。
- 驗證
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>
三. 分區表的其餘操做
3.1 添加新的分區
添加新的分區有2中狀況:
(1)原分區裏邊界是maxvalue或者default。 這種狀況下,咱們須要把邊界分區drop掉,加上新分區後,在添加上新的分區。 或者採用split,對邊界分區進行拆分。
(2)沒有邊界分區的。 這種狀況下,直接添加分區就能夠了。
以邊界分區添加新分區示例:
(1)分區表和索引的信息以下:
SQL> create table custaddr
2 (
3 id varchar2(15 byte) not null,
4 areacode varchar2(4 byte)
5 )
6 partition by list (areacode)
7 (
8 partition t_list556 values ('556') tablespace icd_service,
9 partition p_other values (default)tablespace icd_service
10 );
表已建立。
SQL> create index ix_custaddr_id on custaddr(id)
2 local (
3 partition t_list556 tablespace icd_service,
4 partition p_other tablespace icd_service
5 );
索引已建立。
(2)插入幾條測試數據:
SQL> insert into custaddr values('1','556');
已建立 1 行。
SQL> insert into custaddr values('2','551');
已建立 1 行。
SQL> insert into custaddr values('3','555');
已建立 1 行。
SQL> commit;
提交完成。
SQL> select * from custaddr;
ID AREA
--------------- ----
1 556
2 551
3 555
SQL> select * from custaddr partition(t_list556);
ID AREA
--------------- ----
1 556
SQL>
(3)刪除default分區
sql> alter table custaddr drop partition p_other;
表已更改。
sql> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
table_name partition_name
------------------------------ ------------------------------
custaddr t_list556
(4)添加新分區
SQL> alter table custaddr add partition t_list551 values('551') tablespace icd_service;
表已更改。
SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
CUSTADDR T_LIST556
CUSTADDR T_LIST551
(5)添加default 分區
SQL> alter table custaddr add partition p_other values (default) tablespace icd_service;
表已更改。
SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
CUSTADDR T_LIST556
CUSTADDR T_LIST551
CUSTADDR P_OTHER
(6)對於局部索引,oracle會自動增長一個局部分區索引。驗證一下:
sql> select owner,index_name,table_name,partitioning_type from dba_part_indexes where index_name='ix_custaddr_id';
owner index_name table_name
---------------------- ------------------------------ ------------------
icd ix_custaddr_id custaddr
sql> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='ix_custaddr_id';
index_owner index_name partition_name
------------------------------ ------------------------------ ------------------
icd ix_custaddr_id p_other
icd ix_custaddr_id t_list551
icd ix_custaddr_id t_list556
分區索引自動建立了。
3.2 split 分區拆分
在3.1 中,咱們說明了可使用split的方式來添加分區。 這裏咱們用split方法繼續上面的實驗。
sql> alter table custaddr split partition p_other values('552') into (partition t_list552 tablespace icd_service, partition p_other tablespace icd_service);
表已更改。
--注意這裏紅色的地方,若是是Range類型的,使用at,List使用Values。
SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
CUSTADDR T_LIST556
CUSTADDR T_LIST551
CUSTADDR T_LIST552
CUSTADDR P_OTHER
SQL> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='IX_CUSTADDR_ID';
index_owner index_name partition_name
------------------------------ ------------------------------ ------------------
icd ix_custaddr_id p_other
icd ix_custaddr_id t_list551
icd ix_custaddr_id t_list552
icd ix_custaddr_id t_list556
注意:分區表會自動維護局部分區索引。全局索引會失效,須要進行rebuild。
3.3 合併分區Merge
相鄰的分區能夠merge爲一個分區,新分區的下邊界爲原來邊界值較低的分區,上邊界爲原來邊界值較高的分區,原先的局部索引相應也會合並,全局索引會失效,須要rebuild。
SQL> alter table custaddr merge partitions t_list552,p_other into partition p_other;
表已更改。
SQL> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='IX_CUSTADDR_ID';
index_owner index_name partition_name
-------------------- ------------------------------ ------------------
icd ix_custaddr_id p_other
icd ix_custaddr_id t_list551
icd ix_custaddr_id t_list556
SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
table_name partition_name
------------------------------ ------------------------------
custaddr t_list556
custaddr t_list551
custaddr p_other
3.4 . 移動分區
SQL> alter table custaddr move partition P_OTHER tablespace system;
表已更改。
SQL> alter table custaddr move partition P_OTHER tablespace icd_service;
表已更改。
注意:分區移動會自動維護局部分區索引,oracle不會自動維護全局索引,因此須要咱們從新rebuild分區索引,具體須要rebuild哪些索引,能夠經過dba_part_indexes,dba_ind_partitions去判斷。
SQL> Select index_name,status From user_indexes Where table_name='CUSTADDR';
INDEX_NAME STATUS
------------------------------ --------
IX_CUSTADDR_ID N/A
3.5. Truncate分區
SQL> select * from custaddr partition(T_LIST556);
ID AREA
--------------- ----
1 556
SQL> alter table custaddr truncate partition(T_LIST556);
表被截斷。
SQL> select * from custaddr partition(T_LIST556);
未選定行
說明:
Truncate相對delete操做很快,數據倉庫中的大量數據的批量數據加載可能會有用到;截斷分區一樣會自動維護局部分區索引,同時會使全局索引unusable,須要重建
3.6. Drop分區
SQL> alter table custaddr drop partition T_LIST551;
表已更改。
SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
CUSTADDR T_LIST556
CUSTADDR P_OTHER
一樣會自動維護局部分區索引,同時會使全局索引unusable,須要重建
四. 分區表的索引
分區索引分爲本地(local index)索引和全局索引(global index)。局部索引比全局索引容易管理, 而全局索引比較快。
與索引有關的表:
dba_part_indexes 分區索引的概要統計信息,能夠得知每一個表上有哪些分區索引,分區索引的類型(local/global)
dba_ind_partitions 每一個分區索引的分區級統計信息
dba_indexes/dba_part_indexes 能夠獲得每一個表上有哪些非分區索引
Local索引確定是分區索引,Global索引能夠選擇是否分區,若是分區,只能是有前綴的分區索引。
分區索引分2類:有前綴(prefix)的分區索引和無前綴(nonprefix)的分區索引:
(1)有前綴的分區索引指包含了分區鍵,而且將其做爲引導列的索引。
如:
create index i_id_global on PDBA(id) global --引導列
2 partition by range(id) --分區鍵
3 (partition p1 values less than (200),
4 partition p2 values less than (maxvalue)
5 );
這裏的ID 就是分區鍵,而且分區鍵id 也是索引的引導列。
(2)無前綴的分區索引的列不是以分區鍵開頭,或者不包含分區鍵列。
如:
create index ix_custaddr_local_id_p on custaddr(id)
local (
partition t_list556 tablespace icd_service,
partition p_other tablespace icd_service
)
這個分區是按照areacode來的。可是索引的引導列是ID。 因此它就是非前綴分區索引。
全局分區索引不支持非前綴的分區索引,若是建立,報錯以下:
SQL> create index i_time_global on PDBA(id) global --索引引導列
2 partition by range(time) --分區建
3 (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),
4 partition p2 values less than (maxvalue)
5 );
partition by range(time)
- 第 2 行出現錯誤:
ORA-14038: GLOBAL 分區索引必須加上前綴
4.1 Local 本地索引
對於local索引,當表的分區發生變化時,索引的維護由Oracle自動進行。
注意事項:
(1) 局部索引必定是分區索引,分區鍵等同於表的分區鍵。
(2) 前綴和非前綴索引均可以支持索引分區消除,前提是查詢的條件中包含索引分區鍵。
(3) 局部索引只支持分區內的惟一性,沒法支持表上的惟一性,所以若是要用局部索引去給表作惟一性約束,則約束中必需要包括分區鍵列。
(4) 局部分區索引是對單個分區的,每一個分區索引只指向一個表分區;全局索引則否則,一個分區索引能指向n個表分區,同時,一個表分區,也可能指向n個索引分區,對分區表中的某個分區作truncate或者move,shrink等,可能會影響到n個全局索引分區,正由於這點,局部分區索引具備更高的可用性。
(5) 位圖索引必須是局部分區索引。
(6) 局部索引多應用於數據倉庫環境中。
(7) B樹索引和位圖索引均可以分區,可是HASH索引不能夠被分區。
示例:
sql> create index ix_custaddr_local_id on custaddr(id) local;
索引已建立。
和下面SQL 效果相同,由於local索引就是分區索引:
create index ix_custaddr_local_id_p on custaddr(id)
local (
partition t_list556 tablespace icd_service,
partition p_other tablespace icd_service
)
SQL> create index ix_custaddr_local_areacode on custaddr(areacode) local;
索引已建立。
驗證2個索引的類型:
SQL> select index_name,table_name,partitioning_type,locality,ALIGNMENT from user_part_indexes where table_name='CUSTADDR';
index_name table_name partition locali alignment
------------------------------ ---------- --------- ------ ------------
ix_custaddr_local_areacode custaddr list local prefixed
ix_custaddr_local_id custaddr list local non_prefixed
由於咱們的custaddr表是按areacode進行分區的,因此索引ix_custaddr_local_areacode是有前綴的索引(prefixed)。而ix_custaddr_local_id是非前綴索引。
4.2 Global索引
對於global索引,能夠選擇是否分區,並且索引的分區能夠不與表分區相對應。全局分區索引只能是B樹索引,到目前爲止(10gR2),oracle只支持有前綴的全局索引。
另外oracle不會自動的維護全局分區索引,當咱們在對錶的分區作修改以後,若是對分區進行維護操做時不加上update global indexes的話,一般會致使全局索引的INVALDED,必須在執行完操做後 REBUILD。
注意事項:
(1)全局索引能夠分區,也能夠是不分區索引,全局索引必須是前綴索引,即全局索引的索引列必須是以索引分區鍵做爲其前幾列。
(2)全局索引能夠依附於分區表;也能夠依附於非分區表。
(3)全局分區索引的索引條目可能指向若干個分區,所以,對於全局分區索引,即便只截斷一個分區中的數據,都須要rebulid若干個分區甚至是整個索引。
(4)全局索引多應用於oltp系統中。
(5)全局分區索引只按範圍或者散列分區,hash分區是10g之後才支持。
(6) oracle9i之後對分區表作move或者truncate的時能夠用update global indexes語句來同步更新全局分區索引,用消耗必定資源來換取高度的可用性。
(7) 表用a列做分區,索引用b作局部分區索引,若where條件中用b來查詢,那麼oracle會掃描全部的表和索引的分區,成本會比分區更高,此時能夠考慮用b作全局分區索引。
注意:Oracle只支持2中類型的全局分區索引:
range partitioned 和 Hash Partitioned.
官網的說明以下:
Global Partitioned Indexes
Oracle offers two types of global partitioned index: range partitioned and hash partitioned.
(1)Global Range Partitioned Indexes
Global range partitioned indexes are flexible in that the degree of partitioning and the partitioning key are independent from the table's partitioning method. They are commonly used for OLTP environments and offer efficient access to any individual record.
The highest partition of a global index must have a partition bound, all of whose values are MAXVALUE. This ensures that all rows in the underlying table can be represented in the index. Global prefixed indexes can be unique or nonunique.
You cannot add a partition to a global index because the highest partition always has a partition bound of MAXVALUE. If you wish to add a new highest partition, use the ALTER INDEX SPLIT PARTITION statement. If a global index partition is empty, you can explicitly drop it by issuing the ALTER INDEX DROP PARTITION statement. If a global index partition contains data, dropping the partition causes the next highest partition to be marked unusable. You cannot drop the highest partition in a global index.
(2)Global Hash Partitioned Indexes
Global hash partitioned indexes improve performance by spreading out contention when the index is monotonically growing. In other words, most of the index insertions occur only on the right edge of an index.
(3)Maintenance of Global Partitioned Indexes
By default, the following operations on partitions on a heap-organized table mark all global indexes as unusable:
ADD (HASH)
COALESCE (HASH)
DROP
EXCHANGE
MERGE
MOVE
SPLIT
TRUNCATE
示例1 全局索引,全局索引對全部分區類型都支持:
sql> create index ix_custaddr_ global_id on custaddr(id) global;
索引已建立。
示例2:全局分區索引,只支持Range 分區和Hash 分區:
(1)建立2個測試分區表:
sql> create table pdba (id number, time date) partition by range (time)
2 (
3 partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')),
4 partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')),
5 partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')),
6 partition p4 values less than (maxvalue)
7 );
表已建立。
SQL> create table Thash
2 (
3 id number primary key,
4 item_id number(8) not null
5 )
6 partition by hash(id)
7 (
8 partition part_01,
9 partition part_02,
10 partition part_03
11 );
表已建立。
(2)建立分區索引
示例2:全局分區索引
SQL> create index i_id_global on PDBA(id) global
2 partition by range(id)
3 (partition p1 values less than (200),
4 partition p2 values less than (maxvalue)
5 );
索引已建立。
--這個是有前綴的分區索引。
SQL> create index i_time_global on PDBA(id) global
2 partition by range(time)
3 (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),
4 partition p2 values less than (maxvalue)
5 );
partition by range(time)
- 第 2 行出現錯誤:
ORA-14038: GLOBAL 分區索引必須加上前綴
SQL> create index i_time_global on PDBA(time) global
2 partition by range(time)
3 (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),
4 partition p2 values less than (maxvalue)
5 );
索引已建立。
--有前綴的分區索引
SQL> select index_name,table_name,partitioning_type,locality,ALIGNMENT from user_part_indexes where table_name='PDBA';
index_name table_name partition locali alignment
------------------------------ ---------- --------- ------ ------------
i_id_global pdba range global prefixed
i_time_global pdba range global prefixed
SQL> CREATE INDEX ix_hash ON PDBA (id,time) GLOBAL
2 PARTITION BY HASH (id)
3 (PARTITION p1,
4 PARTITION p2,
5 PARTITION p3,
6 PARTITION p4);
索引已建立。
只要索引的引導列包含分區鍵,就是有前綴的分區索引。
4.3 索引重建問題
(1)分區索引
對於分區索引,不能總體進行重建,只能對單個分區進行重建。語法以下:
Alter index idx_name rebuild partition index_partition_name [online nologging]
說明:
online:表示重建的時候不會鎖表。
nologging:表示創建索引的時候不生成日誌,加快速度。
若是要重建分區索引,只能drop表原索引,在從新建立:
SQL>create index loc_xxxx_col on xxxx(col) local tablespace SYSTEM;
這個操做要求較大的臨時表空間和排序區。
示例:
SQL> select index_name,partition_name from user_ind_partitions where index_name='I_TIME_GLOBAL';
INDEX_NAME PARTITION_NAME
------------------------------ ------------------------------
I_TIME_GLOBAL P1
I_TIME_GLOBAL P2
SQL> alter index I_TIME_GLOBAL rebuild partition p1 online nologging;
索引已更改。
SQL> alter index I_TIME_GLOBAL rebuild partition p2 online nologging;
索引已更改。
(2)全局索引
Oracle 會自動維護分區索引,對於全局索引,若是在對分區表操做時,沒有指定update index,則會致使全局索引失效,須要重建。
SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';
owner index_name table_name status
------------------------------ ------------------------------ ---------- -------
sys ix_pdba_global pdba valid
刪除一個分區:
SQL> alter table pdba drop partition p2;
表已更改。
SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';
owner index_name table_name status
------------------------------ ------------------------------ ---------- -------
sys ix_pdba_global pdba valid
split 分區:
SQL> alter table pdba split partition P4 at(TO_DATE('2010-12-21 00:00:00','YYYY-MM-DD HH24:MI:SS')) into (partition P4, partition P5);
表已更改。
SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';
owner index_name table_name status
------------------------------ ------------------------------ ---------- -------
sys ix_pdba_global pdba valid
drop 分區時使用update indexes
SQL> alter table pdba drop partition P4 UPDATE INDEXES;
表已更改。
SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';
owner index_name table_name status
---------------------- ------------------------------ ---------- -------
sys ix_pdba_global pdba valid
作了幾個drop分區操做,全局索引沒有失效,有點奇怪。 不過若是在生產環境中,仍是當心點。
重建全局索引命令以下:
Alter index idx_name rebuild [online nologging]
示例:
SQL> Alter index ix_pdba_global rebuild online nologging;
索引已更改。
補充一點,分區表存儲空間的問題:
SQL> select table_name,partition_name,tablespace_name from user_tab_partitions where table_name='DBA';
TABLE_NAME PARTITION_NAME TABLESPACE_NAME
---------- ------------------------------ ------------------------------
DBA P1 SYSTEM
DBA P2 SYSTEM
DBA P3 SYSTEM
DBA P4 SYSTEM
經過user_tab_partitions 表能夠查看到每一個分區對應的tablesapce_name. 可是,若是經過all_tables 表,卻查不到分區表對應表空間的信息。
分區表:
SQL> select owner,table_name,tablespace_name,cluster_name from all_tables where table_name='DBA';
OWNER TABLE_NAME TABLESPACE_NAME CLUSTER_NAME
----- ---------- ------------------------------ -----------------------------------------------------
SYS DBA
普通表:
SQL> select owner,table_name,tablespace_name,cluster_name from all_tables where table_name='DAVE';
OWNER TABLE_NAME TABLESPACE_NAME CLUSTER_NAME ----- ---------- ------------------------------ --------------------------------------------------- SYS DAVE SYSTEM