Oracle 11g インターバル・パーティションに関する検証 その2

投稿日: 2008年9月24日

<Oracle 11g インターバル・パーティションに関する検証 その2>
ペンネーム: ミラニスタ

インターバル・パーティションとは、一言で言えば「Oracleが新規レコード
の値を見て、新規パーティションが必要だったら勝手にパーティションを追加
してくれる。」機能です。

▼ 前回のおさらい

ある事例に基づいたフィクションを紹介し、レンジ・パーティションの特徴
と運用上の注意点を挙げました。

1. あるパーティションの上限値としてMAXVALUEを設定すると、そのパーティ
ションは必ず最終パーティションとなる。
2. 1.のパーティションの次に新規パーティションを追加することはできない
ので、パーティションが肥大したらsplit_table_partition句で適正なサイ
ズに分割する必要がある。
3. 上限値としてMAXVALUEを指定しない場合、最終パーティションの上限値を
超えるキー値のデータをInsertしようとすると、ORA-14400エラーが発生す
る。
4. ORA-14400エラーの発生を未然に防ぐには、MAXVALUEでない上限値を指定し
た新規パーティションを予め追加しておく運用が必要である。

インターバル・パーティションと言っても基本はレンジ・パーティションで
あり、新規パーティションを自動的にADD PARTITIONしてくれる発展型に過ぎ
ません。
今回はこのことを理解していただくために、基本的なDDLを紹介し、新規パー
ティションが追加される様子を確認していきます。

▼ とりあえず簡単なインターバル・パーティション表を作成してみる

それでは、一番単純なインターバル・パーティション表を作成してみましょ
う。

Case 1:
テーブル名 : inter01(カラムは c1 numberのみ)
インターバル : 10
第1パーティション
上限値 : 11未満

肝心なインターバルは数値の「10」を指定します。

DDLは以下のようになります。

CREATE TABLE inter01(c1 number)
PARTITION BY RANGE(c1)
INTERVAL(10)                          <==ポイント!
(PARTITION VALUES LESS THAN(11));

また、比較のために通常のレンジ・パーティション表も作成します。
固有上限値のパーティションを1つしか持たない場合(Case 2)と、第2パー
ティションとして上限値にMAXVALUEを指定した場合(Case 3)の2パターンを
作成します。

Case 2:
テーブル名 : range01(カラムは c1 numberのみ)
第1パーティション
上限値 : 11未満

CREATE TABLE range01(c1 number)
PARTITION BY RANGE(c1)
(PARTITION VALUES LESS THAN(11));

Case 3:
テーブル名 : range02(カラムは c1 numberのみ)
第1パーティション
上限値 : 11未満
第2パーティション
上限値 : MAXVALUE

CREATE TABLE range02(c1 number)
PARTITION BY RANGE(c1)
(PARTITION VALUES LESS THAN(11),
 PARTITION VALUES LESS THAN(MAXVALUE));

DDLを比較してみるとインターバル・パーティションというのは、レンジ・
パーティションにINTERVAL句が追加されただけの発展型と言えます。

○ 作成直後

SQL> select TABLE_NAME,PARTITION_NAME,HIGH_VALUE from USER_TAB_PARTITIONS;

TABLE_NAME      PARTITION_NAME  HIGH_VALUE
--------------- --------------- --------------------
INTER01         SYS_P44         11
RANGE01         SYS_P45         11
RANGE02         SYS_P46         11
RANGE02         SYS_P47         MAXVALUE

SQL> select * from INTER01;

レコードが選択されませんでした。

SQL> select * from RANGE01;

レコードが選択されませんでした。

SQL> select * from RANGE02;

レコードが選択されませんでした。

▼ 設定の上限値をInsertした場合の挙動を確認

結果はわかりきっていますが、第1パーティションのHIGH_VALUE「11」を持
つデータは第1パーティションには格納できません。(意味するところは
HIGH_VALUE < 11 なので)
この場合の挙動を確認しておきましょう。

○ RANGE01

SQL> insert into RANGE01 values (11);
insert into RANGE01 values (11)
            *
行1でエラーが発生しました。:
ORA-14400: 挿入されたパーティション・キーはどのパーティションにもマッ
プされません。

予想通りの結果ですね。ORA-14400エラーが発生しました。

○ RANGE02

SQL> insert into RANGE02 values (11);

1行が作成されました。

SQL> commit;

コミットが完了しました。

これは、問題なくInsertできますね。後でちゃんとパーティションSYS_P47
に格納されたかどうか確認します。

○ INTER01

さあ、いよいよインターバル・パーティション表です。

SQL> insert into INTER01 values (11);

1行が作成されました。

おおっ!パーティションSYS_P44の上限値のInsertでも問題なく実行できます。
恐らく新規パーティションが作成されたんでしょう。

▼ 追加されたパーティションを確認する

SQL> select TABLE_NAME,PARTITION_NAME,HIGH_VALUE from USER_TAB_PARTITIONS;

TABLE_NAME      PARTITION_NAME  HIGH_VALUE
--------------- --------------- --------------------
INTER01         SYS_P44         11
INTER01         SYS_P48         21   <==新規に追加されたパーティション
RANGE01         SYS_P45         11
RANGE02         SYS_P46         11
RANGE02         SYS_P47         MAXVALUE

「SYS_P48」という新規パーティションが追加され、HIGH_VALUEには第1パー
ティションのHIGH_VALUE=11にINTERVAL(10)を加算した「21」が設定されてい
ます。

ところで、Insertしたままで、まだcommit/rollbackしてませんでした。
単純にコミットするのは面白くないので、ロールバックさせてみます。つま
り、レコードが存在しない状態に戻ります。

SQL> rollback;

ロールバックが完了しました。

SQL> select * from INTER01;

レコードが選択されませんでした。

SQL> select TABLE_NAME,PARTITION_NAME,HIGH_VALUE from USER_TAB_PARTITIONS;

TABLE_NAME      PARTITION_NAME  HIGH_VALUE
--------------- --------------- --------------------
INTER01         SYS_P44         11
INTER01         SYS_P48         21
RANGE01         SYS_P45         11
RANGE02         SYS_P46         11
RANGE02         SYS_P47         MAXVALUE

レコードが存在しなくても、パーティションはそのまま残っています。
後で、該当するレコードがInsertされる時に備えてパーティションを残して
いるんでしょうね。

なんか、テーブルからレコードをDeleteしても、次に同じキー値をInsert
する時に備えて、インデックスのエントリがそのまま残るのと似ていますね。
<インデックスに関する検証 その7> 参照
https://old.insight-tec.com/mailmagazine/ora3/vol015.html

一旦作成されたものは極力再利用する。コストがかかる都度のメンテナンス
はしない(この場合パーティションの削除)。というOracleのポリシーを感じ
ます。

▼ パーティションに含まれる行数の確認

それでは、今後の検証のために各パーティションに含まれるレコード件数を
確認する方法を紹介しておきます。

dbms_stats.gather_table_statsプロシージャを実行することにより、テー
ブルの統計情報を取得し、パーティションに含まれる行数を確認します。

以下のSQLをテーブル毎に実行します。

execute dbms_stats.gather_table_stats(        -
ownname          => 'INTPART',                - -- 所有者
tabname          => 'INTER01',                - -- テーブル名
partname         => null,                     - -- パーティション名指定無し
estimate_percent => null,                     - -- COMPUTEモード
method_opt       => 'FOR ALL COLUMNS SIZE 1', - -- 列ヒストグラム無し
degree           => null,                     - -- デフォルトのパラレル度
granularity      => 'default',                - -- パーティション/グローバル統計
cascade          => true);                      -- 索引統計も収集

取得した統計情報は以下のSQL文で確認することができます。

SQL> select TABLE_NAME,PARTITION_NAME,NUM_ROWS from USER_TAB_PARTITIONS
     order by 1,2;

TABLE_NAME      PARTITION_NAME    NUM_ROWS
--------------- --------------- ----------
INTER01         SYS_P44                  0
INTER01         SYS_P48                  0
RANGE01         SYS_P45                  0
RANGE02         SYS_P46                  0
RANGE02         SYS_P47                  1

▼ 時間インターバルのインターバル・パーティション表作成

実際にインターバル・パーティションを使う場面を想定した場合、時間イン
ターバルのパーティションの方が多いかもしれません。

今回は、最後に時間インターバルのインターバル・パーティション表作成法
を紹介して締めくくりたいと思います。

テーブル諸元:
  テーブル名          : tmint01(カラムは itime dateのみ)
  インターバル        : 1ヶ月
  第1パーティション
  上限値              : 2008/10/01未満

DDLは次のようになります。

CREATE TABLE tmint01(itime date)
PARTITION BY RANGE(itime)
INTERVAL(numtoyminterval(1,'month'))
(PARTITION VALUES LESS THAN (to_date('2008/10/01','yyyy/mm/dd')));
ポイントは、インターバルの指定方法です。(INTERVAL句)
インターバルの単位によって関数を使い分けます
「年」「月」            :NUMTOYMINTERVAL関数
「日」「時」「分」「秒」:NUMTODSINTERVAL関数

この場合、1ヶ月なので

numtoyminterval(1,’month’)

と指定します。(詳細はSQL 言語リファレンス11g リリース1(11.1)を参照)

○ パーティションの確認

SQL> select TABLE_NAME,PARTITION_NAME,HIGH_VALUE from USER_TAB_PARTITIONS
     where TABLE_NAME='TMINT01';

TABLE_NAME      PARTITION_NAME  HIGH_VALUE
--------------- --------------- ----------------------------------------
TMINT01         SYS_P49         TO_DATE(' 2008-10-01 00:00:00', 'SYYYY-M
                                M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

‘2008-10-01 00:00:00’未満のキー値を持つレコードが、パーティションSYS_P49
に格納されます。

▼ 上限値をInsertした場合の挙動を確認

時間インターバルのインターバル・パーティション表においても、上限値の
Insertに対して新規パーティションが追加されるか確認します。

SQL> insert into TMINT01 values (to_date('2008/10/01','yyyy/mm/dd'));

1行が作成されました。

SQL> commit;

コミットが完了しました。

TABLE_NAME      PARTITION_NAME  HIGH_VALUE
--------------- --------------- ----------------------------------------
TMINT01         SYS_P49         TO_DATE(' 2008-10-01 00:00:00', 'SYYYY-M
                                M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA
TMINT01         SYS_P50         TO_DATE(' 2008-11-01 00:00:00', 'SYYYY-M
                                M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

HIGH_VALUEが’2008-11-01 00:00:00’となるパーティションSYS_P50が追加さ
れています。 ^^

これで、大体の感じはわかっていただけましたでしょうか?

次回はいろいろ変則的なことをやって遊んでみたいと思います。

彼岸でも蝉が鳴いてる温暖化。。。
恵比寿より