表分區(partition):表分區技術是在超大型數據庫(VLDB)中將大表及其索引經過分區(patition)的形式分割爲若干較小、可管理的小塊,而且每一分區可進一步劃分爲更小的子分區(sub partition)。而這種分區對於應用來講是透明的。Oracle的表分區功能經過改善可管理性、性能和可用性,從而爲各式應用程序帶來了極大的好處。一般,分區可使某些查詢以及維護操做的性能大大提升。此外,分區還能夠極大簡化常見的管理任務,分區是構建千兆字節數據系統或超高可用性系統的關鍵工具。es6
分區功能可以將表、索引或索引組織表進一步細分爲段,這些數據庫對象的段叫作分區。每一個分區有本身的名稱,還能夠選擇本身的存儲特性。每一個分區都是一個獨立的段(SEGMENT),能夠存放到相同(不一樣)的表空間中。從數據庫管理員的角度來看,一個分區後的對象具備多個段,這些段既可進行集體管理,也可單獨管理,這就使數據庫管理員在管理分區後的對象時有至關大的靈活性。可是,從應用程序的角度來看,分區後的表與非分區表徹底相同,使用 SQL DML 命令訪問分區後的表時,無需任何修改。(對於高效率查詢是有影響,主要差異是對某一分區數據時行查詢時和對總體數據進行查詢)算法
經過對錶進行分區,能夠得到如下的好處:數據庫
1) 加強可用性:若是表的某個分區出現故障,表在其餘分區的數據仍然可用;oracle
2) 維護方便:若是表的某個分區出現故障,須要修復數據,只修復該分區便可;less
3) 均衡I/O:能夠把不一樣的分區映射到磁盤以平衡I/O,改善整個系統性能;函數
4) 改善查詢性能:對分區對象的查詢能夠僅搜索本身關心的分區,提升檢索速度。工具
分區表事務操做的時候,指定了分區,系統在執行的時候則只操做該分區的記錄,提升了數據處理的速度。不要指定分區直接操做數據也是能夠的。在分區表上建索引及多索引的使用和非分區表同樣。此外,由於在維護分區的時候可能對分區的索引會產生必定的影響,可能須要在維護以後重建索引,相關內容請參考分區表索引部分的文檔性能
分區表相關:已經存在的表沒有方法能夠直接轉化爲分區表。不過 Oracle 提供了在線重定義表的功能。測試
一、表的大小超過2GB。ui
二、表中包含歷史數據,新的數據被增長都新的分區中。
下面的表列出了每一個版本中包括的分區特性:
Oracle數據庫版本 |
分區特性 |
8.0.5 |
引入範圍分區 |
8i |
引入哈希和混合範圍-哈希分區 |
9i |
引入列表分區,混合範圍-列表分區 |
10g |
引入範圍,列表和索引組織表的哈希分區,還引入了其餘混合分區類型(range-hash,range-list) |
11g |
引用分區、間隔分區、虛擬列分區以及擴展的組合分區 引入分區擴展: l 間隔分區 l REF分區 l 基於列的虛擬分區 l 分區顧問 (range-range,list-range,list-list,list-hash) |
ü 範圍分區:將表按某一字段或若干個字段的取值範圍分區。
ü hash分區:將表按某一字段的值均勻地分佈到若干個指定的分區。
ü 複合分區:結合了前面兩種分區類型的優勢,首先經過值範圍將表進行分區,而後以hash模式將數據進一步均勻分配至物理存儲位置。
ü 列表分區:數據分佈是經過分區鍵的一串值定義的,這對不連續的列表很是有用,如:區域、狀態等。(9I 以上支持)
ü 混合分區:有兩個數據分佈辦法用於建立混合分區,表首先經過第一個數據分佈辦法進行初始化分區,而後每一個分區再經過第二個辦法分紅子分區,下面列出了可用的混合分區類型:
範圍-哈希,範圍-列表,範圍-範圍,列表-範圍,列表-列表,列表-哈希。
在10g中索引組織表(表的索引和數據存儲在一塊兒)支持經過範圍、列表或哈希進行分區,然而,混合分區在索引組織表上不受支持。
ü 間隔分區:在11g中才引入,間隔分區是對範圍分區的擴展,爲等距範圍分區提供了自動化,分區建立爲元數據,只有分區開始部分是不變的,附加的段是當數據抵達時才分配的,附加分區和本地索引是自動建立的。
SQL>CREATE TABLE SALES_PART (TIME_ID NUMBER, REGION_ID NUMBER, ORDER_ID NUMBER, ORDER_DATE DATE, SALES_QTY NUMBER(10,2), SALES_AMOUNT NUMBER(12,2) ) PARTITION BY RANGE (ORDER_DATE) INTERVAL (NUMTOYMINTERVAL(1,'month') (PARTITION p_first VALUES LESS THAN ('01-JAN-2006'); |
numtoyminterval函數轉換一個數字爲間隔一年至一個月的文字(年或月),間隔分區表能夠有傳統的範圍和自動間隔部分,範圍分區表能夠經過在ALTER TABLE命令中使用SET INTERVAL選項被擴展爲間隔分區表。
ü REF分區:這個分區方案假設關聯表能從相同的分區策略中受益,子表經過PK-FK(主鍵-外鍵)關係繼承主表的策略,它不須要分區鍵存儲在子表中,經過PARTITION BY REFERENCE關鍵字指定,子表繼承主表的分區策略。
ü 基於列的虛擬分區:在Oracle之前的版本中,只有分區鍵物理存在於表中才能對錶進行分區,在11g中引入一個新的特性「虛擬列」移除了這個限制,容許分區鍵經過使用一個或多個表的列的表達式進行定義,虛擬列僅做爲元數據存儲。如:向表ACCOUNTS添加一個虛擬列:
SQL>CREATE TABLE ACCOUNTS (acc_no number(10) not null, acc_name varchar2(50) not null, acc_loc varchar2(5), acc_branch number(2) generated always as (to_number(substr(to_char(acc_no),1,2))); 使用虛擬列做爲分區鍵: SQL>CREATE TABLE accounts (acc_no number(10) not null, acc_name varchar2(50) not null, acc_loc varchar2(5), acc_branch number(2) generated always as (to_number(substr(to_char(acc_no),1,2))) partition by list (acc_branch); |
ü 分區顧問
Oracle 11g也提供了分區顧問,它支持生成分區建議,與10g中爲物理視圖、物理視圖日誌和索引提供建議相似,實際上,分區顧問是Oracle 11g中SQL訪問顧問的一部分,這個顧問幫助生成建議,它將預先收集實施分區後的性能,它還生成建立高效分區的腳本,能夠手動經過SQL*plus或經過企業管理器隊列提交給Oracle。
將數據基於範圍映射到每個分區,這個範圍是你在建立分區時指定的分區鍵決定的。這種分區方式是最爲經常使用的,而且分區鍵常常採用日期。
當使用範圍分區時,請考慮如下幾個規則:
一、每個分區都必須有一個VALUES LESS THEN子句,它指定了一個不包括在該分區中的上限值。分區鍵的任何值等於或者大於這個上限值的記錄都會被加入到下一個高一些的分區中。
二、全部分區,除了第一個,都會有一個隱式的下限值,這個值就是此分區的前一個分區的上限值。
三、在最高的分區中,MAXVALUE被定義。MAXVALUE表明了一個不肯定的值。這個值高於其它分區中的任何分區鍵的值,也能夠理解爲高於任何分區中指定的VALUE LESS THEN的值,同時包括空值。
範圍分區就是對數據表中的某個值的範圍進行分區,根據某個值的範圍,決定將該數據存儲在哪一個分區上。如根據序號分區,根據業務記錄的建立日期進行分區等。
根據序號分區建表:
SQL> create table dinya_test |
根據交易的序號,交易ID在三千萬如下的記錄將存儲在第一個表空間dinya_space01中,分區名爲:par_01,在三千萬到六千萬之間的記錄存儲在第二個表空間:dinya_space02中,分區名爲:par_02,而交易ID在六千萬以上的記錄存儲在第三個表空間dinya_space03中,分區名爲par_03.
根據交易日期分區建表:
SQL> create table dinya_test |
這樣咱們就分別建了以交易序號和交易日期來分區的分區表。每次插入數據的時候,系統將根據指定的字段的值來自動將記錄存儲到制定的分區(表空間)中。固然,咱們還能夠根據需求,使用兩個字段的範圍分佈來分區,如partition by range ( transaction_id ,transaction_date), 分區條件中的值也作相應的改變。
是在列值上使用散列算法,以肯定將行放入哪一個分區中。當列的值沒有合適的條件時,建議使用散列分區。
散列分區爲經過指定分區編號來均勻分佈數據的一種分區類型,由於經過在I/O設備上進行散列分區,使得這些分區大小一致。散列分區爲經過指定分區編號來均勻分佈數據的一種分區類型,由於經過在I/O設備上進行散列分區,使得這些分區大小一致。如將物料交易表的數據根據交易ID散列地存放在指定的三個表空間中:
SQL> create table dinya_test |
hash分區最主要的機制是根據hash算法來計算具體某條紀錄應該插入到哪一個分區中,hash算法中最重要的是hash函數,Oracle中若是你要使用hash分區,只需指定分區的數量便可。建議分區的數量採用2的n次方,這樣可使得各個分區間數據分佈更加均勻.
有時候咱們須要根據範圍分區後,每一個分區內的數據再散列地分佈在幾個表空間中,這樣咱們就要使用複合分區。複合分區是先使用範圍分區,而後在每一個分區內再使用散列分區的一種分區方法,如將物料交易的記錄按時間分區,而後每一個分區中的數據分三個子分區,將數據散列地存儲在三個指定的表空間中:
SQL> create table dinya_test |
該例中,先是根據交易日期進行範圍分區,而後根據交易的ID將記錄散列地存儲在三個表空間中。
該分區的特色是某列的值只有幾個,基於這樣的特色咱們能夠採用列表分區。
CREATE TABLE PROBLEM_TICKETS
(
PROBLEM_ID NUMBER(7) NOT NULL PRIMARY KEY,
DESCRIPTION VARCHAR2(2000),
CUSTOMER_ID NUMBER(7) NOT NULL,
DATE_ENTERED DATE NOT NULL,
STATUS VARCHAR2(20)
)
PARTITION BY LIST (STATUS)
(
PARTITION PROB_ACTIVE VALUES ('ACTIVE') TABLESPACE PROB_TS01,
PARTITION PROB_INACTIVE VALUES ('INACTIVE') TABLESPACE PROB_TS02
)
這種分區是基於範圍分區和列表分區,表首先按某列進行範圍分區,而後再按某列進行列表分區,分區之中的分區被稱爲子分區。
CREATE TABLE SALES
(
PRODUCT_ID VARCHAR2(5),
SALES_DATE DATE,
SALES_COST NUMBER(10),
STATUS VARCHAR2(20)
)
PARTITION BY RANGE(SALES_DATE) SUBPARTITION BY LIST (STATUS)
(
PARTITION P1 VALUES LESS THAN(TO_DATE('2003-01-01','YYYY-MM-DD'))TABLESPACE rptfact2009
(
SUBPARTITION P1SUB1 VALUES ('ACTIVE') TABLESPACE rptfact2009,
SUBPARTITION P1SUB2 VALUES ('INACTIVE') TABLESPACE rptfact2009
),
PARTITION P2 VALUES LESS THAN (TO_DATE('2003-03-01','YYYY-MM-DD')) TABLESPACE rptfact2009
(
SUBPARTITION P2SUB1 VALUES ('ACTIVE') TABLESPACE rptfact2009,
SUBPARTITION P2SUB2 VALUES ('INACTIVE') TABLESPACE rptfact2009
)
)
這種分區是基於範圍分區和散列分區,表首先按某列進行範圍分區,而後再按某列進行散列分區。
create table dinya_test
(
transaction_id number primary key,
item_id number(8) not null,
item_descriptionvarchar2(300),
transaction_date date
)
partition by range(transaction_date)subpartition by hash(transaction_id) subpartitions 3 store in (dinya_space01,dinya_space02,dinya_space03)
(
partition part_01 values less than(to_date(‘2006-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)
);
使用組合分區 — Oracle8i 數據庫中引入的方案 — 您能夠在分區中建立子分區,從而進一步細分表。但在該狀況下,您只能經過散列子分區對已按範圍分區的表進行再次分區。Oracle9i 中對組合分區進行了擴展,使之包括範圍-列表子分區。
這些方案知足了大多數環境(但並不是全部環境)的須要。例如,假設您有一個名爲 SALES 的包含多列的表,其中包括兩個特殊的列,它們是分區的候選列:state_code,它存儲一個表示銷售狀態的兩位代碼,表面上用於計算銷售稅;以及 product_code,一個用於識別銷售記錄所銷售的產品的三位數字。用戶經過對兩列進行同等的篩選對該表進行查詢,存檔要求也基於這兩列。應用分區決策的原則時,您會發現這兩列都是合適的分區鍵候選者。
在 Oracle 數據庫 11g 中,您能夠至關輕鬆地解決此問題。在該版本中,並不侷限於範圍-散列和範圍-列表組合分區。您的選擇實際上沒有任何限制;您可使用任何組合建立組合分區。
在這個示例中,您能夠決定根據 product_code(由於該列具備更多離散的值)對錶進行列表分區,而後根據 state_code 再次進行列表分區。下面的代碼示例顯示瞭如何實現該操做:
create table sales
(
sales_id number,
product_code number,
state_codevarchar2(2)
)
partition by list (product_code)
subpartition by list (state_code)
(
partition p101 values (101)
(
subpartition p101_ct values ('CT'),
subpartition p101_ny values ('NY'),
subpartition p101_def values (default)
),
partition p201 values (201)
(
subpartition p201_ct values ('CT'),
subpartition p201_ny values ('NY'),
subpartition p201_def values (default)
)
)
選擇並不只限於此處顯示的方法。您還能夠建立列表-範圍組合分區。在上面的示例中,假設產品代碼不是離散的,而是在一個範圍內。您將但願根據 state_code 進行列表分區,而後再根據 product_code 劃分子分區。下面是實現該操做的代碼示例。
create table sales1
(
sales_id number,
product_code number,
state_codevarchar2(2)
)
partition by list (state_code)
subpartition by range (product_code)
(
partition CT values ('CT')
(
subpartition ct_100 values less than (101),
subpartition ct_200 values less than (201)
),
partition NY values ('NY')
(
subpartition NY_100 values less than (101),
subpartition NY_200 values less than (201)
)
)
您也能夠建立範圍-範圍組合分區,若是您有兩個日期域,該方法將很是方便。例如,考慮一個用於銷售處理系統的表,該表包括一個交易日期和一個交貨日期。您可能但願根據一個日期進行範圍分區,而後再根據另外一個日期進行子範圍分區。該方案容許您根據日期進行備份、存檔和清除。
數據庫 11g 中能夠建立如下類型的組合分區:
引用分區經過從父表繼承分區鍵(而非複製鍵列),從而能夠在邏輯上均分具備父子關係的表。分區鍵經過現有的父子關係解析,由現行的主鍵或外鍵約束實施。邏輯相關性還能夠自動級聯分區維護操做,從而使應用程序開發更輕鬆且更不易出錯。
下面是設計分區方案過程當中的一個典型問題:並不是全部表都具備您須要根據其進行分區的列。假設您要建立一個銷售系統,該系統包括兩個簡單的表(sales 和 customers):
create table customers
(
cust_id number primary key,
cust_namevarchar2(200),
rating varchar2(1) not null
)
partition by list (rating)
(
partitionpA values ('A'),
partitionpB values ('B')
);
sales 表的建立以下所示。它是 customers 表的一個子表。
create table sales
(
sales_id number primary key,
cust_id number not null,
sales_amt number,
constraint fk_sales_01
foreign key (cust_id)
references customers
);
理想狀況下,您但願用相同的方式對 sales 表和 customers 表分區:根據 rating 列進行列表分區。但有一個嚴重問題:sales 表沒有名爲 rating 的列!那麼如何根據一個不存在的列進行分區呢?
在 Oracle 數據庫 11g 中,您可使用一個稱爲引用分區的新特性。下面的示例顯示瞭如何將該特性應用於 sales 表:
create table sales
(
sales_id number primary key,
cust_id number not null,
sales_amt number,
constraint fk_sales_01
foreign key (cust_id)
references customers
)
partition by reference (fk_sales_01);
這段代碼建立了與父表 customers 中相同的分區。注意,雖然沒有名爲 rating 的列,但仍根據該列對錶進行了分區。partition by reference (fk_sales_01) 子句包括了分區定義中的外鍵名。該語句指示 Oracle 數據庫 11g 確認經過父表(在該示例中爲 customers)中使用的方案進行了分區。注意 cust_id 列的 NOT NULL 約束;這是引用分區所必需的。
若是您檢查 sales 表中分區的分區邊界:
SQL> select partition_name, high_value
2 fromuser_tab_partitions
3 wheretable_name = 'SALES';
PARTITION_NAME HIGH_VALUE
--------------- -------------------------------
PA
PB
高值爲空,這意味着此處的邊界派生自父表。分區的名稱與父表中的名稱相同。您能夠經過查詢 user_part_tables 視圖來檢查分區的類型。一個名爲 ref_ptn_constraint_name 的特殊列顯示了外鍵約束名稱。
SQL> select table_name, partitioning_type, ref_ptn_constraint_name
2 fromuser_part_tables
3 wheretable_name in ('CUSTOMERS','SALES');
TABLE_NAME PARTITION REF_PTN_CONSTRAINT_NAME
------------------------------ --------- --------------------------
CUSTOMERS LIST
SALES REFERENCE FK_SALES_01
若是您但願按照父表分區的方式對子表進行分區,但沒有相同的列,您又不想僅僅爲了分區而引入這些列,此時引用分區將很是方便。並且,您沒必要針對每一個子表顯式聲明一個很長的分區子句。
範圍分區容許您根據分區鍵列的值的範圍建立分區。下面是一個按範圍分區的表的示例:
create table sales6
(
sales_id number,
sales_dt date
)
partition by range (sales_dt)
(
partition p0701 values less than (to_date('2007-02-01','yyyy-mm-dd')),
partition p0702 values less than (to_date('2007-03-01','yyyy-mm-dd'))
);
您在此處僅針對 2007 年 1 月和 2007 年 2 月定義了分區,若是表中插入一條sales_dt在 2007 年 3 月的記錄,會發生什麼狀況?插入將失敗,並顯示如下錯誤:
ORA-14400: inserted partition key does not map to any partition
顯然,您須要針對 2007 年 3 月添加一個分區,而後才能插入一條記錄。但一般提及來容易作起來難。您一般沒法容忍事先建立大量分區,但其中不多一部分可能會產生此錯誤。
若是 Oracle 以某種方式自動察覺到對新分區的須要,而後建立它們,這樣不是更好嗎?Oracle 數據庫 11g 能夠,它可使用一個稱爲間隔分區的特性。此時,您沒必要定義分區及它們的邊界,只需定義一個定義了每一個分區邊界的間隔。下面是使用間隔分區的示例:
create table sales6
(
sales_id number,
sales_dt date
)
partition by range (sales_dt)
interval (numtoyminterval(1,'MONTH'))
(
partition p0701 values less than (to_date('2007-02-01','yyyy-mm-dd'))
);
注意子句:interval 後面跟着時間間隔。您在此處指示 Oracle 爲每月份建立一個時間間隔。您已經爲 2007 年 1 月的數據建立了名爲 p0701 的初始分區。如今,假設您插入了一條包括 2007 年 6 月數據的記錄:
SQL> insert into sales6 values (1,'01-jun-07');
1 row created.
Oracle 不會返回錯誤,而是成功執行該語句。那麼這條記錄將轉向何處?p0701 分區不能包括該記錄,咱們沒有爲 2007 年 6 月定義分區。但此時,若是您檢查該表的分區:
SQL> select partition_name, high_value
2 fromuser_tab_partitions
3 wheretable_name = 'SALES6';
PARTITION_NAME HIGH_VALUE
--------------- ----------------------------------------------------------------
P0701 TO_DATE(' 2007-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_C
ALENDAR=GREGORIA
SYS_P41 TO_DATE(' 2007-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_C
ALENDAR=GREGORIA
注意名爲 SYS_P一、高值爲 2007 年 7 月 1 日的分區,它最多能夠容納到 6 月底的數據。該分區是由 Oracle 動態建立的,並具備一個系統生成的名稱。
如今,假設您輸入一個小於最高值的值,如 2007 年 5 月 1 日。理想狀況下,它應該具備本身的分區,由於您的分區時間間隔是一個月。
SQL> insert into sales6 values (1,'01-may-07');
1 row created.
SQL> select partition_name, high_value
2 fromuser_tab_partitions
3 wheretable_name = 'SALES6';
PARTITION_NAME HIGH_VALUE
--------------- ----------------------------------------------------------------
P0701 TO_DATE(' 2007-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_C
ALENDAR=GREGORIA
SYS_P41 TO_DATE(' 2007-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_C
ALENDAR=GREGORIA
SYS_P42 TO_DATE(' 2007-06-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_C
ALENDAR=GREGORIA
注意新分區 SYS_P42,其上限爲 6 月 1 日,所以該分區能夠保留 2006 年 5 月的數據。該分區是經過拆分 SYS_P41 分區建立的(針對 6 月份)。所以,當您定義一個間隔分區方案時,Oracle 會自動建立和維護分區。
若是您但願將分區存儲在特定表空間中,可使用 store in 子句執行該操做:
interval (numtoyminterval(1,'MONTH'))
store in (TS1,TS2,TS3)
該子句以循環方式將分區存儲在表空間 TS一、TS2 和 TS3 中。
應用程序開發人員如何定位特定分區?一種方法是知道名稱,這種方法可能不可行,即便您知道名稱,這種方法也很是容易出錯。爲了便於訪問特定分區,Oracle 數據庫 11g 爲分區 SQL 提供了一個新語法:
SQL> select * from sales6 partition for (to_date('15-may-2007','dd-mon-yyyy'));
SALES_ID SALES_DT
---------- ---------
1 01-MAY-07
注意新子句 for (值),它容許您直接引用分區,而沒必要經過它們的確切名稱進行顯式調用。若是您但願截斷或刪除一個分區,能夠調用這個擴展的分段語法。
以此方式建立表以後,DBA_PART_TABLES 視圖中的 PARTITIONING_TYPE 列會顯示時間間隔。
咱們來看另外一個常見問題。在名爲 sales 的表中,您具備如下列:
SQL>desc sales
Name Null? Type
----------------------------------------- -------- ------
SALES_ID NOT NULL NUMBER
CUST_ID NOT NULL NUMBER
SALES_AMT NUMBER
假設您但願按照某個容許您進行清除的方案對該表進行分區,而且基於銷售額進行存檔。如下是銷售的四個類別:
若是 sale_amt 爲 |
且 cust_id 爲 |
則 sale_category 爲 |
0-10000 |
任何內容 |
LOW |
10001-100000 |
0-100 |
LOW |
10001-100000 |
101-200 |
MEDIUM |
10001-100000 |
>200 |
HIGH |
100001-1000000 |
0-100 |
MEDIUM |
100001-1000000 |
101-200 |
HIGH |
100001-1000000 |
>200 |
ULTRA |
>1000000 |
任何內容 |
ULTRA |
您但願根據 sale_category 列對該表進行分區,但有一個問題:沒有名爲 sale_category 的列。這是您從 sale_amt 列派生的列。那麼您如何對該表進行分區呢?
在 Oracle 的早期版本中,您可能已經在表中插入了名爲 sale_category 的新列,並使用一個觸發器用表中所示的邏輯填充該列。可是因爲觸發器,這個新列的存在可能會致使其餘性能影響。
在 Oracle 數據庫 11g 中,一個稱爲虛擬列的新特性使您可以建立一個並不存儲在表中的列,但在運行時將計算該列。您還能夠根據該列進行分區。使用此特性,對該表進行分區就變得垂手可得。
create table sales
(
sales_id number,
cust_id number,
sales_amt number,
sale_categoryvarchar2(6)
generated always as
(
case
whensales_amt<= 10000
then 'LOW'
whensales_amt> 10000
andsales_amt<= 100000
then case
whencust_id< 101 then 'LOW'
whencust_id between 101 and 200 then 'MEDIUM'
else 'MEDIUM'
end
whensales_amt> 100000
andsales_amt<= 1000000
then case
whencust_id< 101 then 'MEDIUM'
whencust_id between 101 and 200 then 'HIGH'
else 'ULTRA'
end
else 'ULTRA'
end
) virtual
)
partition by list (sale_category)
(
partitionp_low values ('LOW'),
partitionp_medium values ('MEDIUM'),
partitionp_high values ('HIGH'),
partitionp_ultra values ('ULTRA')
)
如今,若是您插入如下行:
SQL> insert into sales (sales_id,cust_id,sales_amt) values (1,1,100);
1 row created.
SQL> insert into sales (sales_id,cust_id,sales_amt) values (2,1,1500);
1 row created.
SQL> insert into sales (sales_id,cust_id,sales_amt) values (3,102,1500);
1 row created.
SQL> insert into sales (sales_id,cust_id,sales_amt) values (4,102,10000);
1 row created.
SQL> commit;
Commit complete.
注意,您未輸入sale_category 的值。如今,若是您檢查 p_low 中的記錄,將看到正確的記錄:
SQL> select * from sales partition (p_low);
SALES_ID CUST_ID SALES_AMT SALE_C
---------- ---------- ---------- ------
1 1 100 LOW
該記錄放在相應的分區中。
根據虛擬列分區使您可以建立對業務重要的分區,即便列自己是不存在的。這裏,您已經對虛擬列使用了一個很是簡單的計算方法,但它也能夠如您但願的那樣複雜。在這些狀況下,根據虛擬列進行分區將變得更有價值。
Exchange partition提供了一種方式,讓你在表與表或分區與分區之間遷
移數據,注意不是將錶轉換成分區或非分區的形式,而僅只是遷移表中數
據(互相遷移),因爲其號稱是採用了更改數據字典的方式,所以效率最高(
幾乎不涉及io操做)。Exchange partition適用於全部分區格式,你能夠將
數據從分區表遷移到非分區表,也能夠從非分區表遷移至分區表,或者從
hash partition到range partition諸如此類吧。
其語法很簡單:alter table tbname1 exchange
partition/subpartitionptname with table tbname2;
注意:在將未分區表的數據遷移到分區表中時,可能出現ora-14099的錯誤,雖然能夠用without validation去解決,可是此時進入分區表的數據可能不符合分區規則。因此without validation必定要慎用。
l 涉及交換的兩表之間表結構必須一致,除非附加with validation子
句;
l 若是是從非分區表向分區表作交換,非分區表中的數據必須符合分
區表中指定分區的規則,除非附加without validation子句;
l 若是從分區表向分區表作交換,被交換的分區的數據必須符合分區
規則,除非附加without validation子句;
l Global索引或涉及到數據改動了的global索引分區會被置爲
unusable,除非附加update indexes子句。
提示:
一旦附加了without validation子句,則表示再也不驗證數據有效性,
所以指定該子句時務必慎重。
以上了解了三種分區表的建表方法,下面將使用實際的數據並針對按日期的範圍分區來測試分區表的數據記錄的操做。
SQL> insert into dinya_testvalues(1,12,’BOOKS’,sysdate); |
按上面的建表結果,2006年前的數據將存儲在第一個分區part_01上,而2006年到2010年的交易數據將存儲在第二個分區part_02上,2010年之後的記錄存儲在第三個分區part_03上。
SQL> select * from dinya_test partition(part_01); |
從查詢的結果能夠看出,插入的數據已經根據交易時間範圍存儲在不一樣的分區中。這裏是指定了分區的查詢,固然也能夠不指定分區,直接執行select * from dinya_test查詢所有記錄。
在也檢索的數據量很大的時候,指定分區會大大提升檢索速度。
SQL> update dinya_testpartition(part_01) t set t.item_description=’DESK’ where |
這裏將第一個分區中的交易ID=1的記錄中的item_description字段更新爲「DESK」,能夠看到已經成功更新了一條記錄。可是當更新的時候指定了分區,而根據查詢的記錄不在該分區中時,將不會更新數據,請看下面的例子:
SQL> update dinya_testpartition(part_01) t set t.item_description=’DESK’ where |
指定了在第一個分區中更新記錄,可是條件中限制交易ID爲6,而查詢全表,交易ID爲6的記錄在第三個分區中,這樣該條語句將不會更新記錄。
SQL> delete from dinya_testpartition(part_02) t where t.transaction_id=4; |
上面例子刪除了第二個分區part_02中的交易記錄ID爲4的一條記錄,和更新數據相同,若是指定了分區,而條件中的數據又不在該分區中時,將不會刪除任何數據。
分區表和通常表同樣能夠創建索引,分區表能夠建立局部索引和全局索引。當分區中出現許多事務而且要保證全部分區中的數據記錄的惟一性時採用全局索引。
就是索引信息的存放位置依賴於父表的Partition信息,換句話說建立這樣的索引必須保證父表是Partition
索引信息存放在父表的分區所在的表空間。可是僅能夠建立在父表爲HashTable或者composite分區表的。
LOCAL STORE IN (tablespace) 僅能夠建立在父表爲HashTable或者composite分區表的。而且指定的分區數目要與父表的分區數目要一致
LOCAL STORE IN (tablespace) (PARTITION [partition [LOGGING|NOLOGGING] [TABLESPACE {tablespace|DEFAULT}] [PCTFREE int] [PCTUSED int] [INITRANS int] [MAXTRANS int] [STORAGE storage_clause] [STORE IN {tablespace_name|DEFAULT] [SUBPARTITION [subpartition [TABLESPACE tablespace]]]])
索引信息存放在父表的分區所在的表空間,這種語法最簡單,也是最經常使用的分區索引建立方式。 Local
而且指定的Partition 數目要與父表的Partition要一致
LOCAL (PARTITION [partition
[LOGGING|NOLOGGING]
[TABLESPACE {tablespace|DEFAULT}]
[PCTFREE int]
[PCTUSED int]
[INITRANS int]
[MAXTRANS int]
[STORAGE storage_clause]
[STORE IN {tablespace_name|DEFAULT]
[SUBPARTITION [subpartition [TABLESPACE tablespace]]]])
SQL> create index dinya_idx_t on dinya_test(item_id) |
看查詢的執行計劃,從下面的執行計劃能夠看出,系統已經使用了索引:
SQL> select * from dinya_test partition(part_01) t where t.item_id=12; |
索引信息的存放位置與父表的Partition信息徹底不相干。甚至父表是否是分區表都無所謂的。語法以下:
GLOBAL PARTITION BY RANGE (col_list)
( PARTITION partition VALUES LESS THAN (value_list)
[LOGGING|NOLOGGING]
[TABLESPACE {tablespace|DEFAULT}]
[PCTFREE int]
[PCTUSED int]
[INITRANS int]
[MAXTRANS int]
[STORAGE storage_clause] )
可是在這種狀況下,若是父表是分區表,要刪除父表的一個分區都必需要更新Global Index ,不然索引信息不正確
ALTER TABLE TableName DROP PARTITION PartitionName Update Global Indexes
--查詢索引
select object_name,object_type,tablespace_name,sum(value)
from v$segment_statistics
where statistic_name IN ('physical reads','physicalwrite','logical reads')and object_type='INDEX'
group by object_name,object_type,tablespace_name
order by 4 desc
全局索引創建時global 子句容許指定索引的範圍值,這個範圍值爲索引字段的範圍值:
SQL> create index dinya_idx_t on dinya_test(item_id) |
本例中對錶的item_id字段創建索引分區,固然也能夠不指定索引分區名直接對整個表創建索引,如:
SQL> create index dinya_idx_t on dinya_test(item_id); |
一樣的,對全局索引根據執行計劃能夠看出索引已經可使用:
SQL> select * from dinya_test t where t.item_id=12; |
瞭解了分區表的創建、索引的創建、表和索引的使用後,在應用的還要常常對分區進行維護和管理。平常維護和管理的內容包括:增長一個分區,合併一個分區及刪除分區等等。下面以範圍分區爲例說明增長、合併、刪除分區的通常操做:
ü 增長一個分區
如下代碼給SALES表添加了一個P3分區
ALTERTABLE SALES ADD PARTITION P3 VALUES LESS THAN(TO_DATE('2003-06-01','YYYY-MM-DD'));
注意:以上添加的分區界限應該高於最後一個分區界限。
SQL> alter table dinya_test 2 add partition part_04 values lessthan(to_date(’2012-01-01’,’yyyy-mm-dd’)) tablespacedinya_spa ce03; Table altered. SQL> |
增長一個分區的時候,增長的分區的條件必須大於現有分區的最大值,不然系統將提示ORA-14074 partition bound must collate higher than that of the last partition 錯誤。
ü 刪除分區
如下代碼刪除了P3表分區:
ALTER TABLE SALES DROP PARTITION P3;
在如下代碼刪除了P4SUB1子分區:
ALTER TABLE SALES DROP SUBPARTITION P4SUB1;
注意:若是刪除的分區是表中惟一的分區,那麼此分區將不能被刪除,要想刪除此分區,必須刪除表。
刪除分區表的一個分區後,查詢該表的數據時顯示,該分區中的數據已所有丟失,因此執行刪除分區動做時要慎重,確保先備份數據後再執行,或將分區合併。
如下代碼給SALES表的P3分區添加了一個P3SUB1子分區
ALTER TABLE SALES MODIFY PARTITION P3 ADD SUBPARTITION P3SUB1 VALUES('COMPLETE');
ü 截斷分區
截斷某個分區是指刪除某個分區中的數據,並不會刪除分區,也不會刪除其它分區中的數據。當表中即便只有一個分區時,也能夠截斷該分區。經過如下代碼截斷分區:
ALTER TABLE SALES TRUNCATE PARTITION P2;
經過如下代碼截斷子分區:
ALTER TABLE SALES TRUNCATE SUBPARTITION P2SUB2;
ü 合併分區
合併分區是將相鄰的分區合併成一個分區,結果分區將採用較高分區的界限,值得注意的是,不能將分區合併到界限較低的分區。如下代碼實現了P1 P2分區的合併:
ALTER TABLE SALES MERGE PARTITIONS P1,P2 INTO PARTITION P2;
ü 拆分分區
拆分分區將一個分區拆分兩個新分區,拆分後原來分區再也不存在。注意不能對HASH類型的分區進行拆分。
ALTER TABLE SALES SBLIT PARTITION P2 AT(TO_DATE('2003-02-01','YYYY-MM-DD')) INTO (PARTITION P21,PARTITION P22);
ü 接合分區(coalesca)
結合分區是將散列分區中的數據接合到其它分區中,當散列分區中的數據比較大時,能夠增長散列分區,而後進行接合,值得注意的是,接合分區只能用於散列分區中。經過如下代碼進行接合分區:
ALTER TABLE SALES COALESCA PARTITION;
ü 重命名錶分區
如下代碼將P21更改成P2
ALTER TABLE SALES RENAME PARTITION P21 TO P2;
8、相關查詢
ü 跨分區查詢
select sum( *) from
(select count(*) cn from t_table_SS PARTITION (P200709_1)
union all
select count(*) cn from t_table_SS PARTITION (P200709_2)
);
ü 查詢表上有多少分區
SELECT * FROM useR_TAB_PARTITIONS WHERE TABLE_NAME='tableName'
ü 查詢索引信息
selectobject_name,object_type,tablespace_name,sum(value)
fromv$segment_statistics
wherestatistic_name IN ('physical reads','physicalwrite','logical reads')and object_type='INDEX'
group by object_name,object_type,tablespace_name
order by 4 desc
ü --顯示數據庫全部分區表的信息:
select * from DBA_PART_TABLES
ü --顯示當前用戶可訪問的全部分區表信息:
select * from ALL_PART_TABLES
ü --顯示當前用戶全部分區表的信息:
select * from USER_PART_TABLES
ü --顯示錶分區信息顯示數據庫全部分區表的詳細分區信息:
select * from DBA_TAB_PARTITIONS
ü --顯示當前用戶可訪問的全部分區表的詳細分區信息:
select * from ALL_TAB_PARTITIONS
ü
ü --顯示當前用戶全部分區表的詳細分區信息:
select * from USER_TAB_PARTITIONS
ü --顯示子分區信息顯示數據庫全部組合分區表的子分區信息:
select * from DBA_TAB_SUBPARTITIONS
ü --顯示當前用戶可訪問的全部組合分區表的子分區信息:
select * from ALL_TAB_SUBPARTITIONS
ü --顯示當前用戶全部組合分區表的子分區信息:
select * from USER_TAB_SUBPARTITIONS
ü --顯示分區列顯示數據庫全部分區表的分區列信息:
select * from DBA_PART_KEY_COLUMNS
ü --顯示當前用戶可訪問的全部分區表的分區列信息:
select * from ALL_PART_KEY_COLUMNS
ü --顯示當前用戶全部分區表的分區列信息:
select * from USER_PART_KEY_COLUMNS
ü --顯示子分區列顯示數據庫全部分區表的子分區列信息:
select * from DBA_SUBPART_KEY_COLUMNS
ü --顯示當前用戶可訪問的全部分區表的子分區列信息:
select * from ALL_SUBPART_KEY_COLUMNS
ü --顯示當前用戶全部分區表的子分區列信息:
select* from USER_SUBPART_KEY_COLUMNS
ü --怎樣查詢出oracle數據庫中全部的的分區表
select * from user_tables a where a.partitioned='YES'
ü --刪除一個表的數據是
truncate table table_name;
ü --刪除分區表一個分區的數據是
alter table table_name truncate partition p5;
肯定表分區的分區策約是最重要的,由於隨着數據量的增長,表分區必然存在刪除,擴容,增長等。在這些過程當中還牽涉到全局等索引,由於對分區表進行ddl操做爲破壞全局索引,故全局索引必須在ddl後要重rebuild.
重建整個全局索引或其所有分區。若是已被分區,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 子句
/*
功能:
將一個非分區錶轉換爲分區表
基本思路:
SST_DAY_TOTAL是(數據量上百萬條,列比較多)一個非分區表,此時建立一個與SST_DAY_TOTAL同結構的分區表zhaozhenlong_partition,維護數據,
刪除SST_DAY_TOTAL表,將zhaozhenlong_partition改名爲SST_DAY_TOTA,刪除zhaozhenlong_partition
*/
--具體步驟:
--一、查詢非分區表數據
select to_char(rpt_date, 'yyyymm'), count(*) from SST_DAY_TOTAL group by to_char(rpt_date, 'yyyymm') order by to_char(RPT_DATE, 'yyyymm');
--二、建立分區表(結構和非分區表SST_DAY_TOTAL相同)
create table zhaozhenlong_partition
(
GAS_ID VARCHAR2(12) not null,
RPT_DATE DATE not null,
INV_NO VARCHAR2(12),
......
)
tablespacesdlg
partition by range(rpt_date)(
partition rest values less than (maxvalue))
--三、交換數據(數據從非分區表到分區表)
alter table zhaozhenlong_partition exchange partition rest with table SST_DAY_TOTAL;
--四、查詢分區表數據
select to_char(RPT_DATE, 'mm-yyyy'), count(*) from SST_DAY_TOTAL group by to_char(RPT_DATE, 'mm-yyyy');
--輔助腳本
/*
select 'alter table zhaozhenlong_partition split partition rest at (to_date('''
|| to_char(RPT_DATE, 'yyyymm') ||''',''yyyymm'')) into (partition p'
|| to_char(RPT_DATE, 'yyyymm')
|| ', partition rest);'
fromzhaozhenlong_partition
group by to_char(RPT_DATE, 'yyyymm')
order by to_char(RPT_DATE, 'yyyymm')
*/
--五、對rest分區進行拆分
alter table zhaozhenlong_partition split partition rest at (to_date('200608','yyyymm')) into (partition p200608, partition rest);
alter table zhaozhenlong_partition split partition rest at (to_date('200609','yyyymm')) into (partition p200609, partition rest);
alter table zhaozhenlong_partition split partition rest at (to_date('200610','yyyymm')) into (partition p200610, partition rest);
alter table zhaozhenlong_partition split partition rest at (to_date('200611','yyyymm')) into (partition p200611, partition rest);
alter table zhaozhenlong_partition split partition rest at (to_date('200612','yyyymm')) into (partition p200612, partition rest);
--六、刪除非分區表
drop table SST_DAY_TOTAL
--七、將分區表重命名爲原非分區表名
alter table zhaozhenlong_partition rename to SST_DAY_TOTAL
--八、刪除中間處理過程的分區表
drop table zhaozhenlong_partition
--補充
若是已存在分區表,要將某未分區表(與已分區表結構徹底相同)加入到分區中:
alter table SST_DAY_TOTAL exchange partition p200608 with table zhaozhenlong_1;
alter table SST_DAY_TOTAL exchange partition p200609 with table zhaozhenlong_2;
若是zhaozhenlong_2不符合分區規則,則會報錯,則須要指定without validation 來禁止檢查
alter table SST_DAY_TOTAL exchange partition p200609 with table zhaozhenlong_2 without validation;
固然,若是你確認zhaozhenlong_1表中的數據是符合分區規則的,那指定without validation 以後,不會對zhaozhenlong_1進行全表掃描, 則會縮短exchange時間