バージョン11gでは、範囲が範囲外になると自動的にパーティションを作成するためのいくつかのエキサイティングな新しいパーティションスキーム(間隔パーティションの便利な機能など)が導入されました。
11gより前のバージョンでは、定期的に手動でセクションを追加するか、デフォルトのセクションを分割する必要があります。 つまり、このようなテーブルのステータスを常に監視する必要があります。 この記事では、このようなパーティション分割タスクを自動化するためのソリューションを共有します。
まず、11gの例を示します。
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
-
create table res ( res_id number not null , res_date date , hotel_id number(3), guest_id number ) partition by range (res_id) interval (100) store in (users) ( partition p1 values less than (101) );
このスクリプトは、res_id列の値が1〜100の範囲にあるレコードのp1セクションを作成します。 res_id列の値が101未満のレコードが挿入されると、それらはp1セクションに配置され、新しいレコードのこの列の値が101以上の場合、Oracle Database 11gは新しいセクションを作成し、その名前はシステムによって生成されます。
Oracle Magazineのロシア語版のArup Nandaによる記事の
翻訳で、この例と他の新しいパーティション化スキームの詳細を確認でき
ます 。
以下で説明するソリューションは、パーティションの自動追加をサポートしない他のDBMSに適用できます。
隙間なく均一に増加するパーティションキーのソリューション
テストテーブルを作成します。
- テーブル test_partの作成 (
- ID番号が nullではありません
- name varchar2(100) not null 、
- 所有者varchar2(100) null以外 、
- タイプvarchar2(100) not null 、
- 作成日 が nullではない 、
- 制約 test_part_pk
- 主 キー (id)
- )
- 範囲(id) によるパーティション(パーティションp1 値 は (10000) 未満 );
そのようなテーブルにギャップがない場合、パーティションキーが最大セクションの境界に近づく前に新しいセクションを作成することが望ましいと考えられます。 境界に残したキー値の数、簡単な式に従って簡単に決定できます:
partition_size-(key-start_key_in_partition) 、ここでkeyは現在のパーティションキー、start_key_in_partitionはこのセクションに含まれる最初のキー、partition_sizeはセクション内のキーの数、 and%は整数除算(div)の演算です。 通常、このようなパーティション化は等しいセクションに実行され、これを念頭に置いて、この式を次のように単純化できます:partition_size-key%partition_size。
これにより得られるもの:瞬間を知って、このイベントが発生したときにセクションを追加するトリガーを作成できます。
このトリガーを作成します。
- トリガー tr_test_partを作成 または置換します
- test_part に 挿入 する 前
- 行 ごとに
- when (mod( NEW .id、10000)= 6000)
- 宣言する
- l_part_name番号。
- l_maxvalue番号。
- l_exist番号;
- l_partition_exists 例外 ;
- PRAGMA EXCEPTION_INIT(l_partition_exists、-14074);
- PRAGMA AUTONOMOUS_TRANSACTION;
- 始める
- l_part_name:= ceil(: NEW .ID / 10000)+1;
- 開始
- すぐに 「alter table xtender.test_part add partition p」を 実行し ます || l_part_name || '(より小さい値(' || l_maxvalue || ')' ;
- 例外
- l_partition_existsがnullの 場合 。
- 終了
- end tr_test_part;
このトリガーは、自律型トランザクションを使用して、「P」という名前とセクション番号10,000の新しいセクションを自動的に作成します。IDがセクションキーである場合、セクションの境界まで4,000個の値が残ります(10000-4000 = 6000、つまりID = 6000、 16000、26000など)、ただし、最初に特定のセクションが既に存在するかどうかが確認されます(これは、たとえば、6000番目のレコードを再追加するか、セクションを手動で追加することによって発生します)。 パーティショニングパラメータ-10000および4000。特定の状況に基づいて選択する必要がありますが、境界線(この例では4000)は一度に追加されるレコードの最大数よりも大きくする必要があることに注意してください。 そうでない場合、データ挿入トランザクションの時点で、トランザクションは新しいセクションを「認識」しません。 トランザクションの開始時には存在していなかったため、このキーにマッピングされたセクションが存在しないという苦情とともにデータは挿入されません。 これは、後で説明する
alter table split default_partitionを使用して回避できますが、これはランタイムに影響します。
セクションに記入してトリガーを確認します。
insert into xtender.test_part
select rownum, o.OBJECT_NAME, o.OWNER, o.OBJECT_TYPE, o.CREATED
from all_objects o
where rownum<1000;
さらに、キャッシュのために「ステップ」するシーケンスを使用する場合、セクションの最後から4000〜3900レコードの値のセットに対して実行されるようにトリガーを順番に変更することはできません。
状態を置き換える
when(mod(NEW.id、10000)= 6000)
に
when(mod(NEW.id、10000)between 6000 and 6100)
その他の解決策
デフォルトセクションを指定する場合、レコードがすでにそこにあるときにそれを分離できます。質問は、これを自動的に追跡する方法です。
データディクショナリでは、
dba_tab_partitionsから選択を行うことにより、パーティションテーブルのすべてのセクションに関する情報を取得できます
。partition_positionはテーブル内のセクションの順序を
示し 、
high_valueはセクションパラメーターを
示します。 したがって、テーブルの最後のセクションの名前を取得し、そこから選択して、その中のレコード数を取得できます。
デフォルトセクションで記録が開始されたテーブルに関する情報を受信した後、通知を送信する必要があります。 これを行うには、次のオプションを使用できます。
- アラートを自動的に電子メールに送信するように設定している場合は、alert.logにイベントを書き込むだけです。
- 通知書を送信する手順を書くだけです。
最初のオプションは、2つのパラメーターを持つ
dbms_system.ksdwrtプロシージャを使用して実装されます。
- 最初(BINARY_INTEGER)-書き込み先、可能な値:1-標準トレースファイル、2-alert.log、3-両方;
- 2番目(varchar2)-実際には文字列そのもので、これを記述します。
例:
exec dbms_system.ksdwrt(2、 'テストアラートメッセージ');
2番目のオプションは、
utl_mailパッケージまたはそれ以下のパッケージ
-utl_smtpまたは
utl_tcpを使用すること
です 。
utl_mailは
utl_smtpのより便利なラッパーですが、使用するにはsmtp_out_serverパラメーターを設定する必要があります。 これは、セッション
「ALTER SESSION SET smtp_out_server = ...」およびシステム
「ALTER SYSTEM SET smtp_out_server = ...」に対してすぐに実行できます。
自宅でこのパッケージを見つけられなくても驚かないでください-最初は含まれていません。作成するには、2つのスクリプトを実行する必要があります。
sqlplus sys/<pwd> SQL> @$ORACLE_HOME/rdbms/admin/utlmail.sql SQL> @$ORACLE_HOME/rdbms/admin/prvtmail.plb
すべてを
pkg_partitionsパッケージに
まとめます
パッケージ方法:
- 関数get_penultimate_maxvalue (p_table_owner varchar2、p_table_name varchar2)はvarchar2を返します。
この関数は、所有者パラメーターとしてテーブル名を受け入れ、最後から2番目のセクションの条件値(high_value)を返します。 この情報は、たとえば、最後のセクションがmaxvalueパラメーターを持つセクションである場合に必要になることがあります。したがって、新しいセクションのパラメーターを決定するために最後から2番目のセクションパラメーターが必要になる場合があります。
- 関数get_maxvalued_partitionsは、 tables_props_arrayパイプライン化を返します。
この関数は、テーブルの名前とその最後のセクションが埋め始めた所有者を返します。
使用例:
- 選択する
- p。*、
- sys.pkg_partitions.get_penultimate_maxvalue(p.table_owner、p.table_name)pre_maxvalue
- から
- テーブル (sys.pkg_partitions.get_maxvalued_partitions)p
- function get_maxvalued_partitions_html return varchar2;
この関数はget_maxvalued_partitionsと同じを返しますが、htmlテーブルの形式で返します
- プロシージャsend_partitions_report (メールvarchar2);
最後のセクションに入力し始めたテーブルを含むレポートを送信する手順。 唯一のパラメーターは、送信先のアドレスです。
パッケージコード:
- パッケージ本体の作成 または置換pkg_partitions は
- / **最後から2番目のセクションのパラメーターを返す関数
- * @param i_table_nameテーブル名
- * @ return varchar2
- * /
- 関数 get_penultimate_maxvalue(p_table_owner varchar2、p_table_name varchar2) return varchar2 は
- l_cursor integer default dbms_sql.open_cursor;
- l_ignore number;
- l_long_val varchar2(4000);
- l_long_len番号;
- l_buflen番号:= 4000;
- l_curpos番号:= 0;
- 始める
- dbms_sql.parse(l_cursor、
- ' all.tab_partitions pからp.high_valueを選択します。p.table_ownerのようなもの:oおよびp.table_name like:xおよびp.partition_position =(select max(p1.partition_position)-1 from all_tab_partitions p1 where p.table_owner like:o and p1。 table_name like:x) '
- 、
- dbms_sql.native);
- dbms_sql.bind_variable(l_cursor、 ':x' 、p_table_name);
- dbms_sql.bind_variable(l_cursor、 ':o' 、p_table_owner);
- dbms_sql.define_column_long(l_cursor、1);
- l_ignore:= dbms_sql。 実行 (l_cursor);
- if (dbms_sql.fetch_rows(l_cursor)> 0)
- それから
- dbms_sql.column_value_long(l_cursor、1、l_buflen、l_curpos、
- l_long_val、l_long_len);
- 終了する 場合 ;
- dbms_sql.close_cursor(l_cursor);
- return l_long_val;
- 終わり ;
- / **テーブルの名前とその最後のセクションが埋め始めた所有者を返す関数
- * @ リターン
- * table_name varchar2(4000)、
- * table_owner varchar2(4000)、
- * partitions_count番号、
- * partition_name varchar2(4000));
- * /
- 関数 get_maxvalued_partitions は、 tables_props_arrayパイプライン化を返します
- l_cursor integer default dbms_sql.open_cursor;
- l_count番号。
- l_ignore integer ;
- l_data table_props;
- カーソル l_partitions は
- 選択する
- pl.table_owner、
- pl.table_name、
- count (1)cnt、
- max (pl.partition_name)keep(dense_rank last order by (pl.partition_position))partition_name
- dba_tab_partitions pl から
- pl.table_name は 「BIN $%」と は異なります
- pl.table_owner、pl.table_name による グループ化
- カウント (1)> 1を持つ。
- 開始
- l_partitionsの一部
- ループ
- dbms_sql.parse(l_cursor、
- 「select count(1) from」|| part.table_owner || 「。」 || part.table_name
- || 'パーティション(' || part.partition_name || ')'
- || 'where rownum <2'
- 、
- dbms_sql.native);
- dbms_sql.define_column(l_cursor、1、l_count);
- l_ignore:= dbms_sql.execute_and_fetch(l_cursor);
- dbms_sql.column_value(l_cursor、1、l_count);
- if (l_count> 0) then
- l_data.table_name:= part.table_name;
- l_data.table_owner:= part.table_owner;
- l_data.partitions_count:= part.cnt;
- l_data.partition_name:= part.partition_name;
- パイプ行 (l_data);
- 終了する 場合 ;
- ループの終了。
- 終了
- / **テーブルの名前とその所有者をhtmlの形式で返す関数。最後のセクションが埋め始めた
- * @ リターン
- * table_name varchar2(4000)、
- * table_owner varchar2(4000)、
- * partitions_count番号、
- * partition_name varchar2(4000));
- * /
- function get_maxvalued_partitions_html return varchar2 は
- l_cursor integer default dbms_sql.open_cursor;
- l_count番号。
- l_ignore integer ;
- l_data varchar2(4000);
- カーソル l_partitions は
- 選択する
- pl.table_owner、
- pl.table_name、
- count (1)cnt、
- max (pl.partition_name)keep(dense_rank last order by (pl.partition_position))partition_name
- dba_tab_partitions pl から
- pl.table_name は 「BIN $%」と は異なります
- pl.table_owner、pl.table_name による グループ化
- カウント (1)> 1を持つ。
- 開始
- l_data:= '<html> <body> <table border = 1>'
- || '<tr> <th>テーブル名</ th>'
- || '<th>テーブルの所有者</ th>'
- || '<th>パーティション数</ th>'
- || '<th>パーティション名</ th>'
- || '<th>事前最大値</ th>' ;
- l_partitionsの一部
- ループ
- dbms_sql.parse(l_cursor、
- 「select count(1) from」|| part.table_owner || 「。」 || part.table_name
- || 'パーティション(' || part.partition_name || ')'
- || 'where rownum <2'
- 、
- dbms_sql.native);
- dbms_sql.define_column(l_cursor、1、l_count);
- l_ignore:= dbms_sql.execute_and_fetch(l_cursor);
- dbms_sql.column_value(l_cursor、1、l_count);
- if (l_count> 0) then
- l_data:= l_data || '<tr> <td>'
- || part.table_name
- || '</ td> <td>'
- || part.table_owner
- || '</ td> <td>'
- || part.cnt
- || '</ td> <td>'
- || part.partition_name
- || '</ td> </ tr>' ;
- 終了する 場合 ;
- ループ終了;
- l_data:= l_data || '</ table> </ body> </ html>' ;
- return l_data;
- 終了
- / **
- *テーブルの最後のセクションが埋め始めたレポートを送信する手順
- * /
- プロシージャ send_partitions_report(メールvarchar2)
- は
- msg_body varchar2(4000);
- 開始
- pkg_partitions.get_maxvalued_partitions_html をデュアルから msg_bodyに選択します 。
- -EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server =' 'our_mailserver' '' ;
- utl_mail.send(
- 送信者=> 'oracleDBA@dbdomain.com' 、
- 受信者=>メール、
- 件名=> '最大値パーティションレポート' 、
- メッセージ=> msg_body、
- mime_type => 'text / html' );
- 終了
- pkg_partitionsを終了します。
*このソースコードは、 ソースコードハイライターで強調表示されました。
UPD
zhekapppが
正しくプロンプトを表示した
ため、自動統計収集を有効にするときにnum_rowsを使用できます。 統計収集を有効にするには、dbms_jobsとdbms_stats.gather_table_statsを使用してジョブを追加します。
次に、セクション内のレコード数のリクエストを削除し、リクエストを次のように変更する必要があります。
- 選択する
- pl.table_owner、
- pl.table_name、
- count (1)cnt、
- max (pl.num_rows)keep(dense_rank last order by (pl.partition_position))partition_rows、
- max (pl.partition_name)keep(dense_rank last order by (pl.partition_position))partition_name
- dba_tab_partitions pl から
- pl.table_name は 「BIN $%」と は異なります
- pl.table_owner、pl.table_name による グループ化
この場合の完全なパッケージコードは、
http :
//www.xt-r.com/2010/10/pkgpartitions.htmlにあります。
自動実行
自動実行を設定するためにのみ残ります。 dbms_jobでそれをやってみましょう。
たとえば、データ取得スクリプトの毎日の自動実行:
- 宣言する
- job binary_integer;
- 始める
- dbms_job.submit(
- 仕事、
- 'pkg_partitions.send_partitions_report(' 'dba@domain.ru' ');' 、
- sysdate
- 'trunc(sysdate)+1' );
- dbms_output.put_line(ジョブ);
- 終わり ;