oracle partition的方法

zhengbao_jun發表於2009-03-04

本文章的目的

     瞭解Oracle Partition的使用和技巧

 

本文物件

     具有Oracle的基礎知識,具有一定的Oracle管理知識的DBA管理人員

 

系統環境說明

作業系統 , Win2000

Oracle 版本: 8.1.7

 

分割槽的使用

為什麼要使用分割槽特性.

使用分割槽的主要三個原因﹕

。提高系統的可用性.

。減輕管理負擔.

。提高DML和查詢操作. 

分割槽的模式

Oracle8.1.7版本下存在以下三種分割槽的方法:

﹑範圍分割槽

    partition by range (column_name)

﹑雜湊分割槽

    partition by hash (column_name)

﹑混合分割槽

    partition by range (column_name)

    subpartition  by hash(hash_key_column)

9i後續的版本中新增一種列表分割槽. 以關鍵字List標示.

 

分割槽的索引

像表一樣﹐索引也可以進行分割槽﹐但只有兩種可能的索引分割槽方法。可以﹕

﹑對已分割槽的表中的索引進行均勻分割槽﹐亦稱區域性索引。

    [即索引的分割槽數目與表分割槽的數目是等同的﹐建立這 

     類索引以關索字 Local 來標示]

 

﹑按範圍對索引進行分割槽﹐亦稱全域性索引。

    [即自已重新進行佈局範圍的大小﹐注意這裡僅能利用

     range型﹐不能用 hash list 型別的方法]

 

分割槽的優勢之-提高可用性

可用性來源於這樣一個事實﹕其表雖然在邏輯上是統一的﹐但各分割槽在物理上是獨立的實體﹐可分別存放在不同的磁碟空間上。Oracle優化器知道實現分割槽的方案﹐並且將會相應地從查詢方案中剔除不可用的分割槽。例証﹕

Create table emp2(id ,ename) partition by hash(id) (partition p_1 tablespace indx,partition p_2 tablespace indx1) as select empno,ename from emp;

SQL> select *  from emp2 partition (p_1);

   ID ENAME

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

 7789 SCOTT

 7839 KING

 7876 ADAMS

 7934 MILLER

select *  from emp2 partition (p_2);

ID ENAME

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

 7788 SCOTT

 7844 TURNER

 7900 JAMES

 7902 FORD

Alter tablespace indx offline﹔我們來使表空間 Indx離線.

SQL> select *  from emp2;  這樣查詢會出錯。

ORA-00376: file 6 cannot be read at this time

ORA-01110: data file 6: ‘/orabase/oradata/app/indx01.dbf‘  

SQL> select *  from emp2 where id =7902; 查沒離線的可以

 ID ENAME

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

 7902 FORD

總結﹕將表分割槽後放在不同的所屬表空間是很有必要的﹐當磁碟發生物理損壞時能將故障時間降到最低。例如﹐有一個100G的大表﹐被分割成 50個分割槽﹐當其中一個 分割槽受損﹐那麼現在的恢復時間僅只是恢復這個2G分割槽的所花費的時間﹐也降低了恢復的難度﹐而其它的分割槽的資料依然可能使用當然這要使用有條件的分割槽排除才行。

 

分割槽的優勢之-減輕管理負擔

管理負擔的減輕來源於這樣的事實﹕小表的管理始終優於對大型表的管理。相對於大型物件而言﹐對較小的物件執行操作自然更容易﹑更快且需要的資料會更少。

所謂有管理如我們日常所做的﹕

alter table move…..

Index rebuild ….

表中舊資料的歸檔….

利用分割槽的 split 屬性與單獨表中的資料交換….

 

分割槽的優勢之-DML與查詢增強

增強的DML效能指的是執行並行DML(PDML)的可能。在PDML中﹐Oracle使用非單個連續程式而是多個程式進行處理INSERTUPDATEDELETE。在一個有足夠I/O頻寬的多CPU機器中﹐對於大量的DML操作﹐其潛在的加速效能是巨大的。

PDML雖然有其巨大的效能特別是在 DDS環境下的系統﹐但是我們不能將PDML看作是加速OLTP應用程式的一個特性。因為某些特定的操作須在其特定的環境下才可以的﹐對於某些操作而言並行操作比序列執行可能會慢許多倍。我們知道在並行時會有許多開銷﹐系統要作更多的協調工作。理解OLTP系統﹐其目的是最大程度上地使用機器。

 

它們用於使單個使用者能完全使用機器上所有的磁碟﹑CPU和記憶體。使用者只是在做短時的﹑快速的事務處理。這樣利用並行來增大伸縮性在這兒就完全沒有用途了。因此將其這個特性利用到DDS將會事半功倍。

在查詢效能方面﹐分割槽與兩種型別的特殊操作一起發揮作用﹕

﹑分割槽排除﹕在處理查詢任務時﹐利用謂詞來過濾一些不考慮的分割槽﹐如離線的表空間存在仍能查詢就是利用這

    特性。

﹑並行操作﹕並行索引掃描﹐由此索引分割槽能被並行掃描

 

 

分割槽的優勢之總結

如果要以某種順序來敘述分割槽的好處﹐將會是﹕

1]提高資料的可用性。這對所有的系統型別都有利.

2]通過從資料庫中去除大型物件來減輕對大物件的管理對所有的系統型別都有得.

3]提高某些DML語句和查詢任務的效能主要對大型資料倉儲環境有利.

4]通過將插入操作擴充套件到放多獨立的分割槽[熱點散開]﹐減少高插入所帶到的系統資料爭用現象。

因此﹐通過上述應可以在何種情況下利用分割槽將帶來好的系統效能作出很好的判斷。

 

分割槽的模式理解

範圍分割槽[Range]﹕以這種模式進行分割槽是一種可預見性的對資料範圍的分佈。在後續的管理中簡單方便。特別是在對於歷史資料的處理上很有優勢。但在儲存資料方面不能均勻的分佈。

 

雜湊分割槽[Hash]﹕這種模式適用於沒有可用於分割槽的自然範圍的資料﹐用於對範圍沒有意義的屬性。但這種分割槽能很好的處理I/o問題﹐資料均勻分佈在儲存空間上。

 

濁合分割槽{Range/Hash}﹕這種模式用於當資料合理﹐能以範圍進行區分﹐但分割槽後資料仍然太大難以進行有效的管理這樣用雜湊函式進一步劃分每一個分割槽這將允許在任意個大分割槽中I/O請求擴充套件到多個磁碟。

Oracle9ilist分割槽暫不考慮。

 

分割槽的索引

前面有講﹐像表一樣﹐索引也可以進行分割槽﹐有兩種可能的索引分割槽。

Local Index ﹕這種索引只徵對已經分割槽的表來講的﹐即在已有分割槽的表中的各個小區對應建立索引分割槽。

    create index idx_ptt on p_tt(nm) local

 SQL> select Table_name,PARTITION_NAME ,HIGH_VALUE

     From user_tab_partitions where table_name ='P_TT';

TABLE_NAME     PARTITION_NAME       HIGH_VALUE

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

P_TT          P_1                   10

P_TT          P_2                   20

以上顯示建有一個含兩個分割槽的表.

SQL> select INDEX_NAME,PARTITION_NAME ,HIGH_VALUE

from user_ind_partitions a where a.index_name='IDX_PTT';

INDEX_NAME        PARTITION_NAME           HIGH_VALUE

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

IDX_PTT           P_1                    10

IDX_PTT           P_2                    20

上述顯示同樣的這個索引也是含有兩個分割槽的分割槽索引。

然而就分割槽索引建立時是否含有分割槽鍵又將索引分為﹕

區域性字首索引與區域性非字首索引。

Create index idx_pre on p_tt(id,nm) local;

Create index idx_nopre on p_tt(nm) local;

如果Table p_tt id 來為分割槽Key ﹐那麼第一個索引將稱之為含字首索引﹐第二個為非字首索引。下面來講它們的區別﹕

 

分割槽的索引

這兩種型別的索引都可利用分割槽排除的方法﹐兩者都支援惟一性(只要非字首引包括分割槽碼雖然不是放在前導位置)事實上使用區域性字首索引的查詢任務將總是允許索引分割槽的使用﹐而使用區域性非字首的查詢可能不允許除非在謂詞中有將所有的分割槽鍵含括在其中﹐這就是為什麼區域性非字首索引據說較慢的原因﹕它們不強調分割槽排除﹐雖然支援。所以 Oracle文件強調這個﹕區域性字首索引比區域性非字首引具有更好的效能﹐因它們將檢查的索引數目最小化

下面來例証一下﹕…..

 

分割槽的索引之區域性索引例証

CREATE TABLE partitioned_table

( a int,  b int)  PARTITION BY RANGE (a)

(PARTITION part_1 VALUES LESS THAN(2) ,

PARTITION part_2 VALUES LESS THAN(3))

insert into partitioned_table values ( 1, 1 );

create index local_prefixed on partitioned_table (a,b) local;

alter index local_prefixed modify partition part_2 unusable;

SQL> insert into partitioned_table values ( 2, 1 );

ORA-01502: index 'SCOTT.LOCAL_NONPREFIXED' or partition of such index is in unusable state

上面錯誤可以看出由於分割槽2的索引不可用﹐所以不能新增資料到裡面﹐看sql計劃:

SQL> select * from partitioned_table where a = 1 and b = 1;

執行計畫

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

   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=26)

   1    0   INDEX (RANGE SCAN) OF ‘LOCAL_PREFIXED’ (NON-UNIQUE) (Cost=1 Card=1 Bytes=26)  使用字首索引查詢成功﹐能夠將par_2 從中排除﹐因為指定了 a=1

我們再看第二個例子….. 建立非字首索引.

 

區域性索引例証

create index local_nonprefixed on partitioned_table (b) local;

alter index local_nonprefixed modify partition part_2 unusable;

SQL> select * from partitioned_table where b = 1;

ERROR:ORA-01502: index 'SCOTT.LOCAL_NONPREFIXED' or partition of such

index is in unusable state

這裡看到因為 第二index 是非字首索引﹐它不能將分割槽2從查詢中排除﹐所以Error

再來看看﹐當我們將第一個索引-字首索引 drop 後再來查詢會有什麼結果.

drop index local_prefixed;

SQL> select * from partitioned_table where a = 1 and b = 1;

執行計畫

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

   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=26)

   1    0   TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'PARTITIONED_TABLE'

           (Cost=1 Card=1 Bytes=26)

   2    1     INDEX (RANGE SCAN) OF 'LOCAL_NONPREFIXED' (NON-UNIQUE) (

          Cost=1 Card=1)

 

因為這對於前面那個Error的查詢而言﹐這幾乎是一個相同的查詢方案﹐但這次成功了。那是因為即使沒有字首的索引﹐但因為謂詞中有包含分割槽鍵﹐它能夠將那些用不到的分割槽排除之外。

從上面的例子中可能看出﹐區域性的字首索引與非字首索引在可能情況下沒有什麼很大的區別。 但是在有進行表連線的查詢則結果就可能不同。例﹑﹑﹑

CREATE TABLE range_example

( range_key_column date,  x     int,data      varchar2(20))

PARTITION BY RANGE (range_key_column)

( PARTITION part_1 VALUES LESS THAN (to_date('01-01-1995','dd-mm-yyyy')),

  PARTITION part_2 VALUES LESS THAN (to_date('01-01-1996','dd-mm-yyyy')))

alter table range_example add constraint range_example_pk primary key (range_key_column,x) using index local

 

alter table range_example

add constraint range_example_pk

primary key (range_key_column,x) using index local

insert into range_example values ( to_date('01-01-1994','dd-mm-yyyy'), 1, 'xxx' );

insert into range_example values ( to_date('01-01-1995','dd-mm-yyyy'), 2, 'xxx' );

建立一個區域性字首索引﹐再建立另一個測試所用的表:

create table test ( pk , range_key_column , x,   constraint test_pk primary key(pk) )

as select rownum, range_key_column, x from range_example

SQL> select * from test, range_example  where test.pk = 1

  3    and test.range_key_column = range_example.range_key_column

  4    and test.x = range_example.x;

 

執行計畫

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

   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=47)

   1    0   NESTED LOOPS (Cost=2 Card=1 Bytes=47)

   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1 Bytes=35)

   3    2       INDEX (RANGE SCAN) OF 'TEST_PK' (UNIQUE) (Cost=1 Card=1)

   4    1     PARTITION RANGE (ITERATOR)

   5    4       TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'RANGE_EXAMPLE'(Cost=1 Card=2 Bytes=24)

   6    5         INDEX (UNIQUE SCAN) OF 'RANGE_EXAMPLE_PK' (UNIQUE)

上面查詢方案很清楚的表述了這條sql 所走的執行路徑﹐但當我將前面分割槽表的那個字首索引換成一個非字首的看看有什麼變化。

SQL> alter table range_example

  2  drop constraint range_example_pk

  3  /

已更改表格.

 

alter table range_example add constraint range_example_pk

primary key (x,range_key_column)  using index local;

已更改表格.

SQL> select * from test, range_example   where test.pk = 1

 2    and test.range_key_column = range_example.range_key_column

 3    and test.x = range_example.x;

執行計畫

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

   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=23)

   1    0   NESTED LOOPS (Cost=2 Card=1 Bytes=23)

   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1 Bytes=11)

3    2       INDEX (UNIQUE SCAN) OF 'TEST_PK' (UNIQUE)

   4    1     PARTITION RANGE (ITERATOR)

   5    4       TABLE ACCESS (FULL) OF 'RANGE_EXAMPLE' (Cost=1 Card=46 bytes=552)

從這裡可以看出因為非字首索引在這裡它會認為代價太高﹐所以舍棄用它而用全表掃描來替代。

 

分割槽的索引之全域性索引

全域性索引是使用與它們所在的表不同的模式來進行分割槽的的﹐例這個表可能按timestamp 來分成 10個分割槽﹐而其表中的全域性索引則可以按 region來分成5個分割槽。與區域性索引不同﹐在oracle 中只有一類全域性索引﹐即字首全域性索引。Oracle不支援索引碼不以分割槽碼開頭的全域性索引。例﹕

CREATE TABLE partitioned

( timestamp date,  id        int,  nn        int)

PARTITION BY RANGE (timestamp)

(PARTITION part_1 VALUES LESS THAN

( to_date('01-01-2000','dd-mm-yyyy') ) ,

PARTITION part_2 VALUES LESS THAN

( to_date('01-01-2001','dd-mm-yyyy') ))

上面的表中建立了以 timestamp列來為分割槽鍵的分割槽表﹐有分割槽兩個。接下來我們來建立全域性索引。

 

create index partitioned_index

on partitioned(id)

GLOBAL

partition  by range(id)

(partition part_1 values less than(1000),

partition part_2 values less than (MAXVALUE))

這裡我們建立了在id列上面的全域性索引。注意這個索引中的 maxvalue關鍵字的使用﹐這是因為在全域性索引中必須用於其中﹐它代表的範圍是無限上界﹐確保所有的行都能放至於這個索引之中。

前面有說過在非分割槽的表中也可以存在分割槽索引﹐條件是僅能存在全域性分割槽索引。當然全域性索引的限制[字首方式存在﹑只以基於 range 分割槽型別]也同樣受用。例﹕

SQL> create table test (id number,nm  char);

SQL> create index idx_test on test(id)

global partition by range (id)

(partition p1 values less than (10),

 partition p2 values less than (maxvalue))

 

全域性索引有一個特點﹐當操作其中任一個表分割槽都會造成全域性索引無效則須要重建。

SQL> alter table empp drop partition p4 ;

Table altered

SQL> select *  from empp where job ='ANALYST';

select *  from empp where job ='ANALYST'

ORA-01502: index 'SCOTT.EMP_JOB_IDX' or partition of such index is in unusable state

SQL> alter session set skip_unusable_indexes =true;

Session altered

SQL> select *  from empp where job ='ANALYST';

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO LOC

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

可以利用8.1.5後新加入的引數skip_unusable_indexes 躍過不可用的索引﹐但是這樣就失去了索引所帶來的效能提高。因此最終的方法還是重建索引。

SQL> alter index EMP_JOB_IDX rebuild;

SQL> select *  from empp where job ='ANALYST';

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO LOC

 

分割槽索引之 總結

已經列舉了分割槽索引的兩種型別﹐local/global

根據自己所用的系統來決定那種分割槽索引較適合使用。專家建議﹕資料倉儲應是與區域性索引相匹配。OLTP系統應是與全域性索引相區配。

 

因為在oltp系統中﹐我們要達到的目的是﹕快速訪問﹑資料完整性﹑可用性。全域性索引可以做這件事。Oltp系統的特點與資料倉儲非常不同。我們或許不會做滑動視窗﹐不會分割分割槽﹐不會移動資料等 等。我們在資料倉儲中執行的操作在整體上不會在有效的oltp系統中完成。

如果我們應用的是全域性索引那麼對分割槽任何操作都將引起全域性索引失效﹐那麼全域性索引必須重構{當然在區域性索引中索引也要重構只是僅僅對當事的那個分割槽}等等。

 

例証系統中分割槽的使用

現行的一些系統中就有可能對分割槽的運用﹐如應付憑單檔﹑帳單檔﹑傳票檔等等﹑﹑﹑

所採用的分割槽是以 range 的型別﹐按年月來分﹐每一個季度來切割一個分割槽﹐歷史資料無限期保留沒有進行歸檔處理。對表中建立的索引是沒有進行分割槽處理即是全域性性的索引。

系統中分割槽運用分析﹕

表中資料較大﹐但沒有對歷史資料進行有效的歸檔處理﹐如往年的傳票﹑憑單資料對現階段的操作應沒有很大的關聯﹐所以應單獨開立一個新表進行轉存。這樣能將這些大表始終控制在一個範圍範圍大小之中﹐方便日常管理操作

此外﹐這些表中的索引建立的全都是普通型的全域性索引﹐對 oltp 來講這是好的﹐但卻沒有高效利用分割槽鍵來作為索引的一部份且從未在謂詞中將分割槽鍵作為條件。所以所有的查詢與 dml操作都是在徵對全分割槽進行掃描而沒有利用分割槽剔除的特性﹐這樣給系統帶來很多不必要的I/O操作。

所以系統現在所建立的分割槽表僅只用到了其中的特性之一﹕高可用性。當發生物理故障時可以離線某一表空間有限制的對其它資料時行操作僅此而已。 

分割槽管理示例

以下來示例幾個常用的分割槽管理操作指令﹕

1﹑增加分割槽﹕

SQL> create table pp (id  int,nm  char)

  2  partition by range (id)

  3  (partition p1 values less than (10),

  4   partition p11 values less than (20));

 Table created

SQL> alter table pp add partition p111 values less than (30);

Table altered

以上是基於 range 方式分割槽的增加

需注意的是分割槽的增加只能在最後一個分割槽之後﹐即所界的值必須大於最後一個分割槽的的臨界值﹐這就 add 指令的限定。

SQL> alter table pp2 add partition p111 ;

Table altered

以上是基於 hash 方式分割槽的增加﹐只需指定分割槽的名稱就可以了。

 

2﹑分割槽與表交換。

alter table partitioned

exchange partition fy_1999

with table fy_1999

including indexes

without validation

表示將分割槽 fy_1999與單獨表 fy_1999 間的資料時行至換並且包括索引在內。

3﹑分割槽的滑離

SQL> alter table pp split partition p11 at (15) into (partition sp1,partition sp2);

以上操作僅只能對 range 型態的分割槽表才有用

我們一般會在建立的分割槽表中取最後一個分割槽的臨界值為maxvalue﹐這樣在後續在操作中如果資料範圍變化大的話可以從中滑離出另外的分割槽。

所以這樣看來的話 add split 似乎功能差不多﹐差異在於 split 可以分離另任一個分割槽﹐而 add 僅只能在最後一個分割槽後再新增一個分割槽而已.

 

4drop truncate

這幾類操作同表的操作意思一樣﹑分別表徵刪除其不必要的分割槽或截斷某一分割槽中的資料。

alter table pp drop partitoin sp1

alter table pp truncate  partitoin sp1;

5index 的管理 rebuildunusable

Rebuild 表示當對分割槽進行某些操作後造成 index 無效﹐利用 rebuild來重建這個分割槽索引。Unusable 表示將這個分割槽索引置為無效。

Alter index idx_par1 rebuild partition p_1;

Alter index idx_part1 unusable partiton p_1;

……….

總結﹕上述就是分割槽的大體內容﹐還有很多功能沒有一一列舉﹐可能通來如google,baidu等網站來查詢相關方面的內容介紹及網友提供的各種技巧﹐在管理和實踐中提高水平。

後記

   文件日期:20061101 

---------------------------------------------------------------------------
轉載於網上: http://bbs.stcore.com/thread-1764-1-2.html

關於exchange partition


   exchange partition提供了快速轉換普通成分割槽表的方法,它通過更新資料字典來實現分割槽與普通表的置換,所以速度相當快。
  
  create table t1 as select sysdate dt, all_objects.* from all_objects;
  
  create table t2 as select sysdate dt, all_objects.* from all_objects;
  
  create table t3 as select add_months(sysdate,-24) dt, all_objects.* from all_objects;
  
  create table t( dt, OWNER, OBJECT_NAME, SUBOBJECT_NAME,
  OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE, CREATED,
  LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY,
  GENERATED, SECONDARY )
  partition by range(dt) (
  partition part2003 values less than ( to_date( '01-jan-2004',
  'dd-mon-yyyy') ),
  partition part2004 values less than ( to_date( '01-jan-2005',
  'dd-mon-yyyy') ),
  partition part2005 values less than ( to_date( '01-jan-2006',
  'dd-mon-yyyy') )
  )
  as
  select sysdate dt, all_objects.* from all_objects where 1=0;
  /
  
  SQL 10G>set timing on
  SQL 10G>alter table t
   2 exchange partition part2003
   3 with table t3
   4 /
  Table altered.
  Elapsed: 00:00:00.07
  
  SQL 10G>alter table t
   2 exchange partition part2005
   3 with table t1
   4 /
  Table altered.
  Elapsed: 00:00:00.07
  
  
  SQL 10G>alter table t
   2 exchange partition part2004
   3 with table t2
   4 /
  with table t2
        *
  ERROR at line 3:
  ORA-14099: all rows in table do not qualify for specified partition
  
  Elapsed: 00:00:00.00
  
  因為t2不符合分割槽規則,所以當進行exchange將會報錯,我們可以指定without validation子句來強行禁止oracle檢查合理性
  
  SQL 10G>alter table t
   2 exchange partition part2004
   3 with table t2
   4 without validation
   5 /
  Table altered.
  Elapsed: 00:00:00.03
  
  由於without validation不需要校驗資料的正確性,所以不會對t2做全表掃描,因此exchange的時間將會縮短,without validation子句在進行大表的exchange時效率將會特別高,如果你能確定普通表資料的正確性,那麼請大膽得使用without validation吧。
  
  我們也可以看一下當驗證資料正確性的時候oracle內部是怎麼做的
  
  通過10046 trace events,我們可以看到當進行exchange partition with validation時
  
  select 1 from "T1" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58626;
  
  select 1 from "T2" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58625;
  
  select 1 from "T3" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58624;
  
  oracle通過TBL$OR$IDX$PART$NUM函式來判斷是否普通表滿足分割槽置換的條件
  
  
  SQL 10G>select count(*) from "T1" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58626;
   COUNT(*)
  ----------
       0
  Elapsed: 00:00:00.00
  
  SQL 10G>select count(*) from "T2" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58625;
  
   COUNT(*)
  ----------
     49496
  Elapsed: 00:00:00.04
  
  SQL 10G>select count(*) from "T3" where TBL$OR$IDX$PART$NUM("T", 0, 3,1048576,"DT") != 58624;
  
   COUNT(*)
  ----------
       0
  
  t2是不符合分割槽條件的,所以exchange的時候會報
  ORA-14099: all rows in table do not qualify for specified partition

--------------------------------------------------------------------------
http://enjoytime.blog.hexun.com/2741515_d.html

分割槽表PARTITION table,以及把已存在資料的錶轉為分割槽表。 1.1 分割槽表PARTITION table 在ORACLE裡如果遇到特別大的表,可以使用分割槽的表來改變其應用程式的效能。 1.1.1 分割槽表的建立:某公司的每年產生巨大的銷售記錄,DBA向公司建議每季度的資料放在一個分割槽內,以下示範的是該公司1999年的資料(假設每月產生30M的資料),操作如下: 範圍分割槽表: CREATE TABLE sales (invoice_no NUMBER, ... sale_date DATE NOT NULL ) PARTITION BY RANGE (sale_date) (PARTITION sales1999_q1 VALUES LESS THAN (TO_DATE(‘1999-04-01’,’YYYY-MM-DD’) TABLESPACE ts_sale1999q1, PARTITION sales1999_q2 VALUES LESS THAN (TO_DATE(‘1999-07-01’,’YYYY-MM-DD’) TABLESPACE ts_sale1999q2, PARTITION sales1999_q3 VALUES LESS THAN (TO_DATE(‘1999-10-01’,’YYYY-MM-DD’) TABLESPACE ts_sale1999q3, PARTITION sales1999_q4 VALUES LESS THAN (TO_DATE(‘2000-01-01’,’YYYY-MM-DD’) TABLESPACE ts_sale1999q4 ); --values less than (maxvalue) 列表分割槽表: create table emp ( empno number(4), ename varchar2(30), location varchar2(30)) partition by list (location) (partition p1 values ('北京'), partition p2 values ('上海','天津','重慶'), partition p3 values ('廣東','福建') partition p0 values (default) ); 雜湊分割槽: create table emp ( empno number(4), ename varchar2(30), sal number) partition by hash (empno) partitions 8 store in (emp1,emp2,emp3,emp4,emp5,emp6,emp7,emp8); 組合分割槽:範圍雜湊組合分割槽: create table emp ( empno number(4), ename varchar2(30), hiredate date) partition by range (hiredate) subpartition by hash (empno) subpartitions 2 (partition e1 values less than (to_date('20020501','YYYYMMDD')), partition e2 values less than (to_date('20021001','YYYYMMDD')), partition e3 values less than (maxvalue)); 範圍列表組合分割槽: CREATE TABLE customers_part ( customer_id NUMBER(6), cust_first_name VARCHAR2(20), cust_last_name VARCHAR2(20), nls_territory VARCHAR2(30), credit_limit NUMBER(9,2)) PARTITION BY RANGE (credit_limit) SUBPARTITION BY LIST (nls_territory) SUBPARTITION TEMPLATE (SUBPARTITION east VALUES ('CHINA', 'JAPAN', 'INDIA', 'THAILAND'), SUBPARTITION west VALUES ('AMERICA', 'GERMANY', 'ITALY', 'SWITZERLAND'), SUBPARTITION other VALUES (DEFAULT)) (PARTITION p1 VALUES LESS THAN (1000), PARTITION p2 VALUES LESS THAN (2500), PARTITION p3 VALUES LESS THAN (MAXVALUE)); create table t1 (id1 number,id2 number) partition by range (id1) subpartition by list (id2) (partition p11 values less than (11) (subpartition subp1 values (1)) ); 1.1.2 分割槽表的維護:增加分割槽: ALTER TABLE sales ADD PARTITION sales2000_q1 VALUES LESS THAN (TO_DATE(‘2000-04-01’,’YYYY-MM-DD’) TABLESPACE ts_sale2000q1; 如果已有maxvalue分割槽,不能增加分割槽,可以採取分裂分割槽的辦法增加分割槽!刪除分割槽: ALTER TABLE sales DROP PARTION sales1999_q1; 截短分割槽: alter table sales truncate partiton sales1999_q2; 合併分割槽: alter table sales merge partitons sales1999_q2, sales1999_q3 into sales1999_q23; 分裂分割槽: ALTER TABLE sales SPLIT PARTITON sales1999_q4 AT TO_DATE (‘1999-11-01’,’YYYY-MM-DD’) INTO (partition sales1999_q4_p1, partition sales1999_q4_p2) 交換分割槽: alter table x exchange partition p0 with table bsvcbusrundatald ; 訪問指定分割槽: select * from sales partition(sales1999_q2) EXPORT指定分割槽: exp sales/sales_password tables=sales:sales1999_q1 file=sales1999_q1.dmp IMPORT指定分割槽: imp sales/sales_password FILE =sales1999_q1.dmp TABLES = (sales:sales1999_q1) IGNORE=y 檢視分割槽資訊: user_tab_partitions, user_segments 注:若分割槽表跨不同表空間,做匯出、匯入時目標資料庫必須預建這些表空間。分表區各區所在表空間在做匯入時目標資料庫一定要預建這些表空間!這些表空間不一定是使用者的預設表空間,只要存在即可。如果有一個不存在,就會報錯!預設時,對分割槽表的許多表維護操作會使全域性索引不可用,標記成UNUSABLE。那麼就必須重建整個全域性索引或其全部分割槽。如果已被分割槽,Oracle 允許在用於維護操作的ALTER TABLE 語句中指定UPDATE GLOBAL INDEXES 來過載這個預設特性,指定這個子句也就告訴Oracle 當它執行維護操作的DDL 語句時更新全域性索引,這提供瞭如下好處: 1.在操作基礎表的同時更新全域性索引這就不需要後來單獨地重建全域性索引; 2.因為沒有被標記成UNUSABLE, 所以全域性索引的可用性更高了,甚至正在執行分割槽的DDL 語句時仍然可用索引來訪問表中的其他分割槽,避免了查詢所有失效的全域性索引的名字以便重建它們;另外在指定UPDATE GLOBAL INDEXES 之前還要考慮如下效能因素: 1.因為要更新事先被標記成UNUSABLE 的索引,所以分割槽的DDL 語句要執行更長時間,當然這要與先不更新索引而執行DDL 然後再重建索引所花的時間做個比較,一個適用的規則是如果分割槽的大小小於表的大小的5% ,則更新索引更快一點; 2.DROP TRUNCATE 和EXCHANGE 操作也不那麼快了,同樣這必須與先執行DDL 然後再重建所有全域性索引所花的時間做個比較; 3.要登記對索引的更新併產生重做記錄和撤消記錄,重建整個索引時可選擇NOLOGGING; 4.重建整個索引產生一個更有效的索引,因為這更利於使用空間,再者重建索引時允許修改儲存選項。注意分割槽索引結構表不支援UPDATE GLOBAL INDEXES 子句。 1.1.3 普通表變為分割槽表將已存在資料的普通錶轉變為分割槽表,沒有辦法通過修改屬性的方式直接轉化為分割槽表,必須通過重建的方式進行轉變,一般可以有三種方法,視不同場景使用:用例:方法一:利用原表重建分割槽表。 CREATE TABLE T (ID NUMBER PRIMARY KEY, TIME DATE); INSERT INTO T SELECT ROWNUM, SYSDATE - ROWNUM FROM DBA_OBJECTS WHERE ROWNUM <= 5000; COMMIT; CREATE TABLE T_NEW (ID, TIME) PARTITION BY RANGE (TIME) (PARTITION P1 VALUES LESS THAN (TO_DATE('2000-1-1', 'YYYY-MM-DD')), PARTITION P2 VALUES LESS THAN (TO_DATE('2002-1-1', 'YYYY-MM-DD')), PARTITION P3 VALUES LESS THAN (TO_DATE('2005-1-1', 'YYYY-MM-DD')), PARTITION P4 VALUES LESS THAN (MAXVALUE)) AS SELECT ID, TIME FROM T; RENAME T TO T_OLD; RENAME T_NEW TO T; SELECT COUNT(*) FROM T; COUNT(*) ---------- 5000 SELECT COUNT(*) FROM T PARTITION (P1); COUNT(*) ---------- 2946 SELECT COUNT(*) FROM T PARTITION (P2); COUNT(*) ---------- 731 SELECT COUNT(*) FROM T PARTITION (P3); COUNT(*) ---------- 1096 優點:方法簡單易用,由於採用DDL語句,不會產生UNDO,且只產生少量REDO,效率相對較高,而且建表完成後資料已經在分佈到各個分割槽中了。不足:對於資料的一致性方面還需要額外的考慮。由於幾乎沒有辦法通過手工鎖定T表的方式保證一致性,在執行CREATE TABLE語句和RENAME T_NEW TO T語句直接的修改可能會丟失,如果要保證一致性,需要在執行完語句後對資料進行檢查,而這個代價是比較大的。另外在執行兩個RENAME語句之間執行的對 T的訪問會失敗。 適用於修改不頻繁的表,在閒時進行操作,表的資料量不宜太大。 方法二:使用交換分割槽的方法。 Drop table t; CREATE TABLE T (ID NUMBER PRIMARY KEY, TIME DATE); INSERT INTO T SELECT ROWNUM, SYSDATE - ROWNUM FROM DBA_OBJECTS WHERE ROWNUM <= 5000; COMMIT; CREATE TABLE T_NEW (ID NUMBER PRIMARY KEY, TIME DATE) PARTITION BY RANGE (TIME) (PARTITION P1 VALUES LESS THAN (TO_DATE('2005-9-1', 'YYYY-MM-DD')), PARTITION P2 VALUES LESS THAN (MAXVALUE)); ALTER TABLE T_NEW EXCHANGE PARTITION P1 WITH TABLE T; RENAME T TO T_OLD; RENAME T_NEW TO T; 優點:只是對資料字典中分割槽和表的定義進行了修改,沒有資料的修改或複製,效率最高。如果對資料在分割槽中的分佈沒有進一步要求的話,實現比較簡單。在執行完RENAME操作後,可以檢查T_OLD中是否存在資料,如果存在的話,直接將這些資料插入到T中,可以保證對T插入的操作不會丟失。不足:仍然存在一致性問題,交換分割槽之後RENAME T_NEW TO T之前,查詢、更新和刪除會出現錯誤或訪問不到資料。如果要求資料分佈到多個分割槽中,則需要進行分割槽的SPLIT操作,會增加操作的複雜度,效率也會降低。 適用於包含大資料量的錶轉到分割槽表中的一個分割槽的操作。應儘量在閒時進行操作。 方法三:Oracle9i以上版本,利用線上重定義功能 Drop table t; CREATE TABLE T (ID NUMBER PRIMARY KEY, TIME DATE); INSERT INTO T SELECT ROWNUM, SYSDATE - ROWNUM FROM DBA_OBJECTS WHERE ROWNUM <= 5000; COMMIT; EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE(USER, 'T'); PL/SQL 過程已成功完成。 CREATE TABLE T_NEW (ID NUMBER PRIMARY KEY, TIME DATE) PARTITION BY RANGE (TIME) (PARTITION P1 VALUES LESS THAN (TO_DATE('2004-7-1', 'YYYY-MM-DD')), PARTITION P2 VALUES LESS THAN (TO_DATE('2005-1-1', 'YYYY-MM-DD')), PARTITION P3 VALUES LESS THAN (TO_DATE('2005-7-1', 'YYYY-MM-DD')), PARTITION P4 VALUES LESS THAN (MAXVALUE)); 表已建立。 EXEC DBMS_REDEFINITION.START_REDEF_TABLE(USER, 'T', 'T_NEW'); PL/SQL 過程已成功完成。 EXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE(USER, 'T', 'T_NEW'); PL/SQL 過程已成功完成。 SELECT COUNT(*) FROM T; COUNT(*) ---------- 5000 SELECT COUNT(*) FROM T PARTITION (P3); COUNT(*) ---------- 1096 優點:保證資料的一致性,在大部分時間內,表T都可以正常進行DML操作。只在切換的瞬間鎖表,具有很高的可用性。這種方法具有很強的靈活性,對各種不同的需要都能滿足。而且,可以在切換前進行相應的授權並建立各種約束,可以做到切換完成後不再需要任何額外的管理操作。不足:實現上比上面兩種略顯複雜。 適用於各種情況。 這裡只給出了線上重定義表的一個最簡單的例子,詳細的描述和例子可以參考下面兩篇文章。 Oracle的線上重定義表功能:http://blog.itpub.net/post/468/12855 Oracle的線上重定義表功能(二):http://blog.itpub.net/post/468/12962 XSB: 把一個已存在資料的大表改成分割槽表:第一種(表不是太大): 1.把原表改名: rename xsb1 to xsb2; 2.建立分割槽表: CREATE TABLE xsb1 PARTITION BY LIST (c_test) (PARTITION xsb1_p1 VALUES (1), PARTITION xsb1_p2 VALUES (2), PARTITION xsb1_p0 VALUES (default)) nologging AS SELECT * FROM xsb2; 3.將原表上的觸發器、主鍵、索引等應用到分割槽表上; 4.刪除原表: drop table xsb2; 第二種(表很大): 1. 建立分割槽表: CREATE TABLE x PARTITION BY LIST (c_test) [range ()] (PARTITION p0 VALUES [less than ](1) tablespace tbs1, PARTITION p2 VALUES (2) tablespace tbs1, PARTITION xsb1_p0 VALUES ([maxvalue]default)) AS SELECT * FROM xsb2 [where 1=2]; 2. 交換分割槽 alter table x exchange partition p0 with table bsvcbusrundatald ; 3. 原表改名alter table bsvcbusrundatald rename to x0; 4. 新表改名alter table x rename to bsvcbusrundatald ; 5. 刪除原表drop table x0; 6. 建立新表觸發器和索引create index ind_busrundata_lp on bsvcbusrundatald(。。。) local tablespace tbs_brd_ind ; 或者: 1. 規劃原大表中資料分割槽的界限,原則上將原表中近期少量資料複製至另一表; 2. 暫停原大表中的相關觸發器; 3. 刪除原大表中近期資料; 4. 改名原大表名稱; 5. 建立分割槽表; 6. 交換分割槽; 7. 重建相關索引及觸發器(先刪除之再重建). 參考指令碼: select count(*) from t1 where recdate>sysdate-2 create table x2 nologging as select * from t1 where recdate>trunc(sysdate-2) alter triger trg_t1 disable delete t1 where recdate>sysdate-2 commit rename t1 to x1 create table t1 [nologging] partition by range(recdate) (partition pbefore values less than (trunc(sysdate-2)), partition pmax values less than (maxvalue)) as select * from x1 where 1=2 alter table t1 exchange partition pbefore with table x1 alter table t1 exchange partition pmax with table x2 drop table x2 [重建觸發器] drop table x1 1.1.4 參考材料:如果表中預期的資料量較大,通常都需要考慮使用分割槽表,確定使用分割槽表後,還要確定什麼型別的分割槽(range partition、hash partition、list partition等)、分割槽區間大小等。分割槽的建立最好與程式有某種默契,偶曾經建立分割槽表,按自然月份定義分割槽的,但程式卻在查詢時預設的開始時間與結束時間是:當前日期-30至當前日期,比如當天是9.18號,那查詢條件被產生為8.18-9.18,結果分割槽後並不沒有大幅提高效能,後來對程式的查詢日期做了調整,按自然月查詢,系統的負載小了很多。從Oracle8.0開始支援表分割槽(MSSQL2005開始支援表分割槽)。 Oracle9i 分割槽能夠提高許多應用程式的可管理性、效能與可用性。分割槽可以將表、索引及索引編排表進一步劃分,從而可以更精細地對這些數

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/235507/viewspace-559937/,如需轉載,請註明出處,否則將追究法律責任。

相關文章