Articles
完全なパーティション化Oracle Database 11gでのパーティション化の選択は、ほぼ無制限です。
"分割統治" - これこそ、Oracle データベースのパーティション化を説明するのにもっとも適した言葉です。バージョン8以降、表や索引を複数のセグメントにパーティション化して、異なる表領域に配置できるようになりました。表は論理要素的には一つですが、個々のパーティションは別々のセグメントとして保存されます。これによって、データの操作が容易になります。 Oracle Database 11gの参照パーティション化、インターバル・パーティション化、仮想列のパーティション化、拡張コンポジット・パーティション化などの拡張機能によって、無制限のパーティション化設計を実現して管理性を高めることができます。 パーティション化の基本やパーティション化列またはパーティション化スキームの選択に関して学習する際は、2006年9/10月発行の『Oracle Magazine』に掲載されている 筆者の記事をご覧ください。 拡張コンポジット・パーティション化Oracle8i Databaseで導入されたパーティション化スキームであるコンポジット・パーティション化を使用すると、パーティションからサブパーティションを作成できるので、表をさらに細分化できます。ただし、このリリースでは、レンジ・パーティション化された表をハッシュ・サブパーティション化できるだけです。さらにOracle9i Databaseの拡張コンポジット・パーティション化では、レンジ-リスト・サブパーティション化も可能です。 これらのパーティション化スキームは、ほとんどの状況に対応しています。たとえば、パーティション化の対象となる2つの特別な列(消費税を計算するための状態を示した2桁のコードを格納するstate_codeと製品を識別する3桁の数値のproduct_code)を含む多くの列を持つSALES表を利用します。ユーザーは、2つの列を同じようにフィルタリングして表への問合せを実行します。アーカイブ要件もこの2つの列に基づきます。パーティション化決定の原則を適用すると、この2つの列はパーティション化キーとして最適な選択肢となります。 Oracle Database 11gを使用すると、問題を簡単に解決できます。このリリースは、レンジ-ハッシュおよびレンジ-リスト・コンポジット・パーティション化のみに制限されません。つまり、選択がほぼ無制限になります。あらゆる組合せのコンポジット・パーティションを作成できます。 この例の場合、product_codeで表をリスト・パーティション化できます。この列には多くの個別の値があるので、state_codeでサブパーティション化(リスト・パーティション化)します。次のサンプル・コードは、この実行方法です。
create table sales
(
sales_id number,
product_code number,
state_code varchar2(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)
)
)
選択肢は、例に示されている内容に制限されることはありません。リスト-レンジ・コンポジット・パーティション化も作成できます。たとえば、product_codeが個別ではなく多くの範囲を含むとします。state_codeでリスト・パーティション化して、product_codeでサブパーティション化できます。実行方法を示すサンプル・コードは、以下のとおりです。
create table sales1
(
sales_id number,
product_code number,
state_code varchar2(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)
)
)
レンジ-レンジ・コンポジット・サブパーティションも作成できます。これは、2つの日付フィールドを使用する場合に非常に有効です。たとえば、トランザクションの日付と配送の日付を含む売上処理システムの表があります。一方の日付でレンジ・パーティション化して、もう一方の日付でレンジ・サブパーティション化できます。このパーティション化スキームによって、日付に基づくバックアップ、アーカイブ、および消去を実行できます。
Oracle Database 11gでは、次のタイプのコンポジット・パーティションを作成できます。
参照パーティション化パーティション化スキームを設計する際に一般的な問題となるのは、パーティション化に必要な同一の列をすべての表が含んでいるわけではないことです。2つの簡単な表(SALESとCUSTOMERS)を使用した売上システムを作成するとします。
create table customers
(
cust_id number primary key,
cust_name varchar2(200),
rating varchar2(1) not null
)
partition by list (rating)
(
partition pA values ('A'),
partition pB 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
);
CUSTOMERS表と同じ方法(rating列でリスト・パーティション化)で、SALES表をパーティション化するとします。しかし、これには深刻な問題があります。SALES表にrating列が存在しないからです。存在しない列でどうやってパーティション化すればよいのでしょうか。
Oracle Database 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 Database 11gは、親表(この場合、CUSTOMERS表)に使用されるパーティション化スキームごとにパーティション化が実行されることを確認します。cust_id列のNOT NULL制約に注意してください。これは参照パーティション化に必要です。
SALES表のパーティションでパーティション境界を確認する場合 SQL> select partition_name, high_value 2 from user_tab_partitions 3 where table_name = 'SALES'; PARTITION_NAME HIGH_VALUE --------------- ------------------------------- PA PB最大値はNULLです。つまり、境界は親表から導かれているのです。パーティション名は、親表のものと同じになります。user_part_tablesビューへの問合せを実行して、パーティション化のタイプを確認できます。ref_ptn_constraint_nameと呼ばれる特別な列は、外部キーの制約名を示しています。
SQL> select table_name, partitioning_type, ref_ptn_constraint_name
2 from user_part_tables
3 where table_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月と2月のパーティションのみを定義しています。2007年3月のsales_dtを含む表にレコードが挿入されるとどうなるでしょう。次のエラーが発生して挿入は失敗します。
ORA-14400: inserted partition key does not map to any partitionレコードを挿入する前に、2007年3月のパーティションを追加する必要があります。しかし、"言うは易し行うは難し"です。パーティションを多めに作成しておくことはできないでしょう。少なすぎるとこのエラーが発生します。 パーティションの必要性を検出して自動的にパーティションを作成できれば、それは非常に効率的です。Oracle Database 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'))
);
間隔を示す句に注意してください。ここでは、1ヶ月ごとの間隔を指示しています。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 from user_tab_partitions
3 where table_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
最大値が2007年7月1日の、6月末までのデータに対応するSYS_P41パーティションに注意してください。このパーティションはOracleデータベースによって動的に作成され、システムによって生成された名前が付けられています。
ここで、2007年5月1日などの最大値よりも低い値を入力する場合を仮定します。パーティション間隔が月単位なので、固有のパーティションが作成されるはずです。
SQL> insert into sales6 values (1,'01-may-07');
1 row created.
SQL> select partition_name, high_value
2 from user_tab_partitions
3 where table_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
上限が6月1日の新しいパーティションSYS_P42に注意してください。このパーティションには、2007年5月のデータを格納できます。SYS_P41パーティション(6月用)の分割によって、このパーティションが作成されました。インターバル・パーティション化スキームを定義する場合、Oracleデータベースはパーティションを自動的に作成して保守します。
特定の表領域にパーティションを格納する場合、store in句を使用して実行できます。 interval (numtoyminterval(1,'MONTH')) store in (TS1,TS2,TS3)この句は、ラウンド・ロビン方式でTS1、TS2、およびTS3表領域にパーティションを格納します。 アプリケーション開発者が特定のパーティションを指定するにはどうすればよいでしょう。名前を取得できるかもしれませんが、エラーが発生する可能性があります。特定のパーティションへのアクセスを容易にするため、Oracle Database 11gはパーティション指定について、新しい構文を提供しています。
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列には、INTERVALが表示されます。 システム・パーティション化この機能を実際に使用することはあまりないかもしれませんが、たいへん優れているのでここで説明しておきます。 まれではあるものの、想定されるユースケースです。論理的な方法でパーティション化できない表があるとします。結果として大規模な表になり、拡張索引メンテナンスやその他の操作が必要になるなどの問題が生じます。 このため、開発者は解決手段を提供します。表をとにかくパーティション化できるならば、開発者は知的な方法によるパーティションへの書込みを約束します。これにより、アプリケーションは、特定のレコードを格納するパーティションを制御できます。DBAは、そのパーティションを定義する必要があります。次に例を示します。 create table sales3 ( sales_id number, product_code number, state_code number ) partition by system ( partition p1 tablespace users, partition p2 tablespace users );パーティション・キーや境界は存在しないので注意してください。表は物理的に2つのセグメントに分割されますが、1つの論理表のままです。この方法で定義すると、データベースは、単一の大規模な表の代わりに表の2つのセグメントを作成します。次のコードで確認できます。 SQL> select partition_name 2 from user_segments 3 where segment_name = 'SALES3'; PARTITION_NAME ------------------------------ P1 P2ローカル索引を作成する場合も、同じようにパーティション化されます。 SQL> create index in_sales3_state on sales3 (state_code) local; Index created. SQL> select partition_name 2 from user_segments 3 where segment_name = 'IN_SALES3_STATE'; PARTITION_NAME ------------------------------ P1 P2user_part_tablesにチェックインして、パーティション化のタイプを確認できます。 SQL> select partitioning_type 2 from user_part_tables 3 where table_name = 'SALES3'; PARTITION --------- SYSTEMこれによって、システム・パーティション化を示すSYSTEMが表示されます。このようなタイプの表のhigh_value列値はNULLなので注意してください。 SQL> select partition_name, high_value 2 from user_tab_partitions 3 where table_name = 'SALES3'; PARTITION_NAME HIGH_VALUE -------------- --------------------- P1 P2さて、レンジ、リスト、ハッシュなどのパーティション化キーやパーティション化スキームが存在しない場合、Oracleデータベースは入力レコードを格納するパーティションをどのように把握するのでしょうか。 Oracleデータベースはパーティションを把握しないというのが答えです。レコードを表に挿入する場合の例を以下に示します。
SQL> insert into sales3 values (1,101,1);
insert into sales3 values (1,101,1)
*
ERROR at line 1:
ORA-14701: partition-extended name or bind variable must be used for DMLs on
tables partitioned by the System method
パーティションの境界がわからないので、アプリケーションでパーティションを指定して、データの挿入時にこの情報を提供しなければなりません。この文を次のように再度書き込む必要があります。
SQL> insert into sales3 partition (p1) values (1,101,1); 1 row created.削除する場合、パーティションを指定する必要はありませんが、この場合はパーティションの境界の概念が存在しないことに注意してください。次のように文を発行する場合 SQL> delete sales3 where state_code = 1;Oracleデータベースはすべてのパーティションをスキャンして、レコードの存在する場所を確認する必要があります。これを回避するには、次のように書き込む必要があります。 SQL> delete sales3 partition (p1) where state_code = 1;更新の場合も同じです。これにより、レコードが検索されるパーティションを制限します。 論理的な方法で表をパーティション化できない場合、システム・パーティション化によって大きな利点が得られます。パーティション化の利点を活用して、開発者はレコードを格納するパーティションを自由に決定できるのです。 単一パーティションの表領域のトランスポート以前のバーションのOracle データベースでは、表領域をトランスポートし、その後同じまたは異なるデータベースにプラグインできました。このプロセスにはデータファイルのコピーが含まれており、データベース間のデータを転送するもっとも高速な方法となっています。しかし、これまで単一のパーティションの表領域をトランスポートしてプラグインすることはできませんでした。Oracle Database 11gではこれが可能です。 CT、NYなどの複数のパーティションを含むSALES5表を使用するとします。 SQL> select partition_name, tablespace_name 2 from user_tab_partitions 3 where table_name = 'SALES5'; PARTITION_NAME TABLESPACE_NAME -------------- --------------- CT TS1 NY TS2ここで、次のコマンドを使用してCTパーティションをトランスポートできます。 $ expdp tables=scott.sales5:ct transportable=always directory=data_pump_dir dumpfile=p_ct.dmp Export: Release 11.1.0.4.0 - Beta on Sunday, 10 June, 2007 16:05:40 Copyright (c) 2003, 2005, Oracle. All rights reserved. Username: / as sysdba Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.4.0 - Beta With the Partitioning, Oracle Label Security, OLAP, Data Mining and Oracle Database Vault options Starting "SYS"."SYS_EXPORT_TABLE_01": /******** AS SYSDBA tables=scott.sales5:ct transportable= always directory=data_pump_dir dumpfile=p_ct.dmp Processing object type TABLE_EXPORT/TABLE/PLUGTS_BLK Processing object type TABLE_EXPORT/TABLE/TABLE Processing object type TABLE_EXPORT/TABLE/END_PLUGTS_BLK Master table "SYS"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded **************************************************************************** Dump file set for SYS.SYS_EXPORT_TABLE_01 is: /home/oracle/oracle/admin/PROBE2/dpdump/p_ct.dmp ****************************************************************************** Datafiles required for transportable tablespace TS1: /home/oracle/oradata/PROBE2/PROBE2/ts1_01.dbf Job "SYS"."SYS_EXPORT_TABLE_01" successfully completed at 16:05:55これらの2つのファイル(p_ct.dmpとts1_01.dbf)を別のシステムに移動して、そのデータベースにプラグインできます。ここでは学習の目的で、同じデータベースにプラグインします。最初に、表と表領域のts1を削除する必要があります。 SQL> drop table scott.sales5; Table dropped. SQL> drop tablespace ts1 including contents; Tablespace dropped.表領域をデータベースにプラグインします。ただし、ここで少し問題が発生します。sales5表が存在しないので、表全体ではなく単一のパーティション(CT)だけが最初にエクスポートされたのです。存在しない表の単一のパーティションをどうすればインポートできるでしょうか。 これは、Oracle Database 11gのpartition_optionsと呼ばれるData Pump Importの新しいコマンドライン・オプションによって可能になります。departition値を指定すると、Oracle Data Pumpは、エクスポートされたパーティションから新しい表を作成します。この方法によってパーティションを"中止"する意味合いから、departition(パーティション解除)という名前になっています。次に動作を確認します。 $ impdp partition_options=departition dumpfile=p_ct.dmp transport_datafiles='/home/oracle/oradata/PROBE2/PROBE2 /ts1_01.dbf' Import: Release 11.1.0.4.0 - Beta on Sunday, 10 June, 2007 21:58:08 Copyright (c) 2003, 2005, Oracle. All rights reserved. Username: / as sysdba Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.4.0 - Beta With the Partitioning, Oracle Label Security, OLAP, Data Mining and Oracle Database Vault options Master table "SYS"."SYS_IMPORT_TRANSPORTABLE_04" successfully loaded/unloaded Starting "SYS"."SYS_IMPORT_TRANSPORTABLE_04": /******** AS SYSDBA partition_options= departition dumpfile=p_ct.dmp transport_datafiles=/home/oracle/oradata/PROBE2/PROBE2/ts1_01.dbf Processing object type TABLE_EXPORT/TABLE/PLUGTS_BLK Processing object type TABLE_EXPORT/TABLE/TABLE Processing object type TABLE_EXPORT/TABLE/END_PLUGTS_BLK Job "SYS"."SYS_IMPORT_TRANSPORTABLE_04" successfully completed at 21:58:23このSQLによって、sales5_ct表が作成されます。これは、トランスポータブル表領域によって先にエクスポートされたSALES5表のCTパーティションです。名前のとおり、表の名前は元の表とパーティション名を組み合わせたものです。DBA_SEGMENTSビューを確認して、セグメントの存在を確認できます。 SQL> select segment_name 2 from dba_segments 3 where tablespace_name = 'TS1'; SEGMENT_NAME ----------------- SALES5_CT単一パーティションのトランスポータブル表領域機能を使用して、異なるデータベースに表の単一パーティションをプラグインできます。その後、パーティション変更操作を実行して、その表のパーティションとして使用できます。 仮想列のパーティション化別の一般的な問題について説明します。SALES表に次の列を使用します。 SQL> desc sales Name Null? Type ----------------------------------------- -------- ------ SALES_ID NOT NULL NUMBER CUST_ID NOT NULL NUMBER SALES_AMT NUMBER 売上に基づいた消去およびアーカイブできるパーティション化スキームでこの表をパーティション化するとします。売上には4つのカテゴリがあります。
Oracleデータベースの以前のバージョンでは、sale_categoryという新しい列を表に持ち、列を移入するためにトリガーを使用しました。しかし、新しい列によって、トリガー動作による別のパフォーマンスの問題が発生する可能性がありました。 仮想列と呼ばれるOracle Database 11gの新しい機能を使用すると、表に格納されずに実行時に計算される列を作成できます。この列でパーティション化することも可能です。この機能によって、この表のパーティション化が容易になります。
create table sales
(
sales_id number,
cust_id number,
sales_amt number,
sale_category varchar2(6)
generated always as
(
case
when sales_amt <= 10000
then 'LOW'
when sales_amt > 10000
and sales_amt <= 100000
then case
when cust_id < 101 then 'LOW'
when cust_id between 101 and 200 then 'MEDIUM'
else 'MEDIUM'
end
when sales_amt > 100000
and sales_amt <= 1000000
then case
when cust_id < 101 then 'MEDIUM'
when cust_id between 101 and 200 then 'HIGH'
else 'ULTRA'
end
else 'ULTRA'
end
) virtual
)
partition by list (sale_category)
(
partition p_low values ('LOW'),
partition p_medium values ('MEDIUM'),
partition p_high values ('HIGH'),
partition p_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
レコードが適切なパーティションに配置されました。
仮想列のパーティション化は、列自体が存在しなくてもビジネス用の適切なパーティションを作成できます。ここでは、非常に簡単な計算の仮想列を使用しましたが、複雑にすることもできます。この場合、仮想列のパーティション化の価値はさらに高まります。 Partition Advisorパーティション化スキームを設計する際の最大の考慮事項は、パーティション化スキームとパーティション化列の選択です。これは、広範なワークロード分析を行う熟練した専門家に任せるべき課題です。ただし、専門家でも正しく判断できない場合があります。データとアクセス方式を分析してパーティション化スキームを提案するPartition Advisorは、Oracle Database 11gの新しく非常に有効なアドバイザです。このツールの詳細は、今後のこの記事で確認してください。 結論パーティション化はもっとも便利な手段の1つですが、Oracle Database 11gを使用するとさらに便利になります。
"Oracle Database 11g:DBAと開発者のための主要な機能"ホームページに戻る
|