pl/sql コーディングtips oracle database 10g...
Post on 04-Jun-2018
249 Views
Preview:
TRANSCRIPT
Page-1
PL/SQL コーディングTipsOracle Database 10g 対応版
2005/05/02第2版
日本オラクル株式会社
本資料はOTN Japanの同タイトルの資料を6年ぶりに改定したものです。Oracle Database 10g R1に対応しています。
Page-2
2
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-3
3
PL/SQLとは
Procedural Language / Structured Query Language(SQLへの手続き型言語拡張)
アプリケーション
beginSQL文
ifSQL文
elseSQL文
end if;end;
PL/SQLは、SQL言語(標準的リレーショナル・データベース言語)には無いプログラミング構造を提供します。
SQL言語を手続き型言語として機能拡張したものです。
Page-4
4
PL/SQLとは - 利点
プロシージャ機能
エラー処理
パフォーマンスの向上
高い移植性
Oracleとの統合
プロシージャ機能
•変数、定数の利用
•SQLとビジネスロジックがシームレスに記述できる
エラー処理
•エラー(例外)を補足、処理が行える
パフォーマンスの向上
•複数のSQLを一度にOracleに送信(特にネットワーク環境では効果有り)
高い移植性
•Oracleの稼働環境すべてに移行可能(Platformに依存しない)
Oracleとの統合
•すべてのSQLデータ型をサポート
•データベース列の定義を基にした変数の宣言
Page-5
5
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-6
6
組み込みパッケージ
DBMS_OUTPUTUTL_FILEDBMS_SQLDBMS_PIPEDBMS_ALERTDBMS_JOBDBMS_UTILITYその他
代表的な組み込みパッケージについて、その用途とサンプルを紹介します。組み込みパッケージの詳細についてはマニュアル『PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス 10g リリース1(10.1) 』もご参照ください。
Page-7
7
DBMS_OUTPUTバッファ経由でメッセージをやりとりする
– 同一セッション内(別アプリから利用可)– FIFO
1回のPUT/GETは255バイト以内
バッファの大きさは2,000バイト
– ENABLEプロシージャで1,000,000バイトまで拡張可能
AP1バッファ
PUT_LINE
GET_LINEAP2
DBMS_OUTPUTは変数などの値やメッセージなどをバッファに入出力するためのパッケージです。
以下、検索結果を標準出力に表示するサンプル実行例です。
SQL*Plus、ServerManagerでは以下のように“ set serveroutput on”としてください。それ以外では、GET_LINEで取り出す必要があります。
SQL> set serveroutput on
SQL> begin
2 select deptno into :info from dept where loc = 'BOSTON';
3 dbms_output.put_line('Value: ' ||:info);
4 end;
5 /
Value: 40
PL/SQLプロシージャが正常に完了しました。
Page-8
8
UTL_FILEOracleサーバー上のテキストファイルの読み書きを行う
– Oracle9iR2からファイルのコピーや移動、削除も可能
改行コードは自動付加/削除– 1行は最大1,024バイト(改行コード含む)– Oracle8.0.5から32,767バイト(改行コード含む)まで可
FOPENプロシージャでサイズを指定
以下の事前設定が必要– 初期化パラメータUTL_FILE_DIRの設定
– Oracle9iR2からはDIRECTORYオブジェクトの利用も可(推奨)
プログラムのインプットとアウトプットとして、サーバOS上のテキストファイルを使用することで、PL/SQLプログラム以外との情報の受け渡しが可能になります。UTL_FILEパッケージはテキストファイルのオープン、クローズ、リード、ライトの機能を提供します。
安全を保証する為に、ファイルへアクセスするには、初期化パラメータファイルUTL_FILE_DIRにアクセス対象のフォルダ(ディレクトリ)を指定しておく必要が有ります。
例)UTL_FILE_DIR = C:¥TEMP
複数のディレクトリを指定する場合は、間に他のパラメータを挟まずにUTL_FILE_DIRを複数行指定します。
例)UTL_FILE_DIR = C:¥TEMP
UTL_FILE_DIR = D:¥TEST
上記例で間に異なる初期化パラメータの指定があると、後で指定した方(例だとD:¥TEST)のみが有効になります。全ディレクトリを対象にする場合は「UTL_FILE_DIR = * 」と指定します。また、インスタンスを起動したOSユーザーが指定されたディレクトリやファイルに対してOSレベルでI/Oを許可されている必要があります。
Oracle9iR2からはUTL_FILE_DIRの他に、DIRECTORYオブジェクトを指定することもできます。 UTL_FILE_DIRの変更はインスタンスの再起動を必要としますが、DIRECTORYオブジェクトでは再起動の必要はありません。
例)SQL> create directory dir1 as 'c:¥temp';
Page-9
ファイル入出力の手順
① ファイルをOPENする --- FOPEN② ファイル操作
ファイルから読み込む --- GET_LINE ファイルに書き出す --- PUT, PUT_LINE, PUTF, NEW_LINE③ ファイルをCLOSEする --- FCLOSE, FCLOSE_ALL
以下、ディレクトリ(c:¥temp)にファイル(aaa.txt)を作成し、1行書き出し、その内容を読み込み出力するサンプルです。
set serveroutput on
DECLARE
filehndl UTL_FILE.FILE_TYPE;
filedata varchar2(100);
BEGIN
/* 書き込みモードでOPEN */
filehndl := UTL_FILE.FOPEN('c:¥temp','aaa.txt','w');
/* ファイルにHello Worldと出力 */
UTL_FILE.PUT_LINE(filehndl,'Hello World');
UTL_FILE.FCLOSE(filehndl);
/* 読込モードでOPEN */
filehndl := UTL_FILE.FOPEN('c:¥temp','aaa.txt','r');
/* ファイルの内容を1行読み込む */
UTL_FILE.GET_LINE(filehndl,filedata);
/* ファイルから読み込んだ内容の表示*/
DBMS_OUTPUT.PUT_LINE(filedata);
UTL_FILE.FCLOSE(filehndl);
EXCEPTION
WHEN UTL_FILE.INVALID_OPERATION THEN
UTL_FILE.FCLOSE(filehndl);
RAISE_APPLICATION_ERROR(-20051,'Invalid Operation');
END;
/
文字コード・改行コードについて:
書き出される文字コードと改行コードはサーバーの環境に依存します。具体的には、文字コードはデータベースのキャラクタセット、改行コードはサーバーのOS(シェル)で使用している文字コードに依存します。また、環境変数NLS_LANGはデータベースのキャラクタ・セットに合わせてください。合っていない場合の本パッケージの入出力結果は不定になります。例えばUNIX系OSでデータベース・キャラクタセット JA16EUCの場合、日本語EUCの改行コード(0x0A)が付加されます。
Page-10
10
DBMS_SQL
動的SQLを発行する
– 動的SQL:アプリ実行時にバインド変数以外の内容(テーブル名等)が確定するSQL
– DDL文もDBMS_SQLを通じて発行する
Oracle8i以降はシステム固有の動的SQL文
の利用を推奨
Oracle8以前のPL/SQLでは、動的SQLやDDL文を発行するためにはDBMS_SQLというパッケージを使用する必要があります。
動的SQLとは、コンパイル時は未完成(条件等が決まっていない)ようなDML文や、DDL文のことを表わします。
実行時に、条件などが指定できるのでプログラムに汎用性を持たせることができます。
Oracle8iからはEXECUTE IMMEDIATE文やOPEN-FOR文といった、PL/SQLの文法として動的SQLやDDL文の発行がサポートされるようになりました(システム固有の動的SQL文といいます)。以下DBMS_SQLを利用したケースとシステム固有の動的SQL文を利用したケースでコーディングがどう変わるかを同じ内容の処理を通じて比較します。
Page-11
まずはDBMS_SQLを使用した場合のサンプルです。
declaresource varchar2(30) := 'emp';emp_no emp.empno%type;emp_name emp.ename%type;emp_sal emp.sal%type;sel_cursor integer;ins_stmt integer;row_cnt integer;
beginsel_cursor := dbms_sql.open_cursor;dbms_sql.parse(sel_cursor, 'select empno,ename,sal from '|| source, dbms_sql.native);dbms_sql.define_column(sel_cursor, 1, emp_no);dbms_sql.define_column(sel_cursor, 2, emp_name, 10);dbms_sql.define_column(sel_cursor, 3, emp_sal);ins_stmt := dbms_sql.open_cursor;dbms_sql.parse(ins_stmt, 'insert into temp values (
:ins_empno, :ins_ename, :ins_sal)', dbms_sql.native);row_cnt := dbms_sql.execute(sel_cursor);while (dbms_sql.fetch_rows(sel_cursor) > 0) loop
dbms_sql.column_value(sel_cursor, 1, emp_no);dbms_sql.column_value(sel_cursor, 2, emp_name);dbms_sql.column_value(sel_cursor, 3, emp_sal);dbms_sql.bind_variable(ins_stmt, 'ins_empno', emp_no);dbms_sql.bind_variable(ins_stmt, 'ins_ename', emp_name);dbms_sql.bind_variable(ins_stmt, 'ins_sal', emp_sal);row_cnt := dbms_sql.execute(ins_stmt);
end loop;dbms_sql.close_cursor(sel_cursor);dbms_sql.close_cursor(ins_stmt);
end;/
同じ処理内容をシステム固有の動的SQL文を利用して記述すると、以下の様にシンプルになります。declaresource varchar2(30) := 'emp';type sel_cursor_type is ref cursor;sel_cursor sel_cursor_type;emp_no emp.empno%type;emp_name emp.ename%type;emp_sal emp.sal%type;
beginopen sel_cursor for
'select empno, ename, sal from ' || source;loop
fetch sel_cursor into emp_no, emp_name, emp_sal;exit when sel_cursor%notfound;execute immediate 'insert into temp values (:empno, :ename, :sal)'using emp_no, emp_name, emp_sal;
end loop;close sel_cursor;
end;/
Page-12
12
DBMS_PIPE
パイプ(SGA)を使ったセッション間通信
Oracle SGA
Database
メッセージ送信セッション
メッセージ受信セッション
同一インスタンス内のプログラムでお互いにメッセージのやり取りができます。非同期に、いつでもメッセージを受け取ることが可能です。SGAにメッセージ情報を記録しますので、SHUTDOWNを行うと消えてしまいます。
DBMS_PIPEパッケージ・ファンクション(一部)
PACK_MESSAGE(メッセージアイテム)ローカル・バッファにメッセージを作成
SEND_MESSAGE(パイプ名,タイムアウト,最大パイプサイズ)名前付きパイプにメッセージを送信。名前付きパイプが存在しない場合には、パブリック・パイプが暗黙的に作成される。
RECEIVE_MESSAGE(パイプ名,タイムアウト)名前付きパイプからローカル・バッファへメッセージをコピー。
UNPACK_MESSAGE(メッセージアイテム)バッファ内の次の項目へアクセス。
PURGE(パイプ名)名前付きパイプの内容を削除。
Page-13
以下、PIPEを使用した簡単なサンプルプログラムです。
SQL*Plusを2つ立ち上げ、接続します。(ユーザーは同じでなくても可)
一方を<メッセージ送信セッション>、もう片方を<メッセージ受信セッション>として以下を実行してください。
<メッセージ受信セッション>
declare
pipe_stat integer;
item_val varchar2(10);
begin
/* メッセージを受け取りメッセージ・バッファにコピー */
pipe_stat :=DBMS_PIPE.RECEIVE_MESSAGE('pipe1',30);
/* メッセージ・バッファから変数に格納 */
DBMS_PIPE.UNPACK_MESSAGE(item_val);
DBMS_OUTPUT.PUT_LINE(item_val);
/* パイプの内容を空にする */
DBMS_PIPE.PURGE('pipe1');
end;
/
PIPE
<メッセージ送信セッション>
declare
pipe_stat integer;
begin
/*項目をメッセージ・バッファに詰め込む*/
DBMS_PIPE.PACK_MESSAGE('HELLO!!');
/* メッセージ・バッファの内容をパイプに送信 */
pipe_stat :=DBMS_PIPE.SEND_MESSAGE('pipe1',10);
end;
/
実際の利用例としては、ストアド・プロシージャ等から送信されたメッセージをPro*Cプログラムで受信し、OS上のコマンドを起動するといったことが考えられます。表を使って、メッセージのやり取りを行う方法も考えられますが、送信時にCOMMITを
発行しなければならないので、他の処理に影響が及ぶ可能性が有るという点に注意する必要が有ります。PIPEではその必要は有りません。
Page-14
14
DBMS_ALERTイベント通知形式のアプリ間通信
OracleDatabase
アプリケーションA アプリケーションB(WAIT中)
EMP表トリガー
EMP_TRIG
更新
更新の通知
DBMS_ALERTパッケージで提供されるアラート機能を使用して、データベースイベントの通知を行うことが可能です。トリガーの機能に似ていますが、トリガーはDML文の実施ごとに発生するのに対し,アラートはトランザクション処理がコミットされた時点で発行されます。(トランザクションがロールバックされるとアラートは発生しません)
上の例では、アプリケーションAによるEMP表の更新を自動的にアプリケーションBに通知する例になります。
DBMS_PIPEとの違いは、
• DBMS_ALERTはシグナルを送るトランザクションがCOMMITされてからアラートを送る。
• アラート発生時にユーザーの要求が無ければ受け取らない。
• (DBMS_ALERTはWAITしつつアラートを待つのに対して、DBMS_PIPEはメッセージをPIPEに取りにいく)
ということです。
Page-15
以下サンプルです。
実行方法は、以下の通りです。
SQL*Plusを2つ立ち上げ、同じユーザーで接続します。
1. 一方のSQL*Plusから、TESTREGISTER.SQLを実行します。
2. もう一方から、TESTSIGNAL.SQLを実行します。
TESTREGISTER.SQL
----------------
set serveroutput on;
declare
namex varchar(50) :='aaa';
msg varchar(50) := '';
status number := 99;
timeout number := 60;
begin
/* アラートに関心のあることを登録 */
dbms_alert.register(namex);
commit;
/* シグナルを待つ(タイムアウト60秒) */
dbms_alert.waitany(namex,msg,status,timeout);
dbms_output.put_line('name=' || namex);
dbms_output.put_line('msg=' || msg);
dbms_output.put_line('status=' || status || ',timeout=' || timeout);
end;
/
TESTSIGNAL.SQL
--------------
declare
name varchar(50) := 'aaa';
msg varchar(50) := 'テスト・メッセージ';
begin
dbms_alert.signal(name,msg);
commit;
end;
/
----------------- 以下、実行結果 -----------------------
SQL> @TESTREGISTER.SQL
name=AAA
msg=テスト・メッセージ
status=0,timeout=60
PL/SQLプロシージャが正常に完了しました。
DBMS_ALERT.REGISTER(アラート名)
DBMS_ALERT.WAITANY(アラート名,メッセージ,ステータス,タイムアウト)
DBMS_ALERT.SIGNAL(アラート名,メッセージ)
Page-16
16
DBMS_JOBジョブのスケジューリング
– ジョブの1回実行、反復実行
– ジョブをバックグラウンド、並列実行
SUBMIT --------- ジョブをキューに送る
REMOVE --------- キューから指定したジョブを削除する
CHANGE --------- 指定したジョブを変更する
[ジョブの定義、ジョブの実行時刻、ジョブの実行間隔]
WHAT ----------- 指定したジョブのジョブの定義を変更
NEXT_DATE ------ 指定したジョブの次の実行時刻を変更
INTERVAL ------- 指定したジョブの実行間隔を変更
BROKEN --------- ジョブの実行を禁止にする
RUN ------------ 指定したジョブを強制的に実行させる
SNP バックグラウンド・プロセス
待ち行列に入っている実行予定のジョブキューを定期的に起動し、実行します。SNPプロセスの設定はINIT.ORAの以下のパラメータで行います。
JOB_QUEUE_PROCESSES : プロセス数。
JOB_QUEUE_INTERVAL : インスタンスのSNPバックグラウンド・プロセスの起動する間隔を設定する。
ジョブキューの情報は以下のディクショナリから、取得できます。
DBMS_JOB_RUNNING : 現在実行中のJOB
DBA_JOBS,USER_JOBS : JOBの一覧
以下、ジョブをキューに送る例です。1日1回proc1プロシージャが実行されます。DECLARE
JOB# BINARY_INTEGER;BEGIN
DBMS_JOB.SUBMIT(JOB#,’proc1’,SYSDATE,’SYSDATE+1’);END;
Page-17
17
DBMS_UTILITYちょっとしたコマンドを網羅
ANALYZE_SCHEMA スキーマ・オブジェクトのアナライズ(解析)COMPILE_SCHEMA スキーマ・オブジェクトのコンパイルCOMMA_TO_TABLE カンマ区切りの文字列をPL/SQL表に格納TABLE_TO_COMMA PL/SQL表からカンマ区切りの文字列に移動FORMAT_ERROR_STACK 現在のエラースタック入手(参照:Page45,46)FORMAT_CALL_STACK 現在のコール・スタック入手(参照:Page44)GET_TIME 経過時間取得GET_CPU_TIME CPU時間取得( Oracle10g以降)PORT_STRING Oracle、OS情報取得EXEC_DDL_STATEMENT DDL文を実行(Oracle8i以降)
ANALYZE_SCHEMA、COMPILE_SCHEMA指定されたオブジェクトをアナライズ/コンパイルします。
スキーマ全体の一括処理
COMMA_TO_TABLE,TABLE_TO_COMMAPL/SQL表 ←→ カンマ区切り文字列
declare
out_tabb dbms_utility.uncl_array;
begin
:in_list := 'scott,emp,ename';
dbms_utility.comma_to_table(:in_list,:out_tablen,out_tabb);
......
例えば、CSV形式のファイルを読み込んで加工するときなどに使用します。
PORT_STRINGOracleのバージョン(init.oraのCOMPATIBLEの内容)、OS情報
(例)SunSolaris2.6上で、Oracle8.0.5の実行例: SVR4-be-8.0.0WindowsNT4.0上のOracle8.1.5の実行例: IBMPC/WIN_NT-8.1.0
Page-18
18
その他便利なパッケージ(1)DBMS_LOCK.SLEEP
– 特定時間セッションを中断(秒数を指定)
DBMS_RANDOM(Oracle8~)– RANDOMプロシージャ
範囲はバージョンにより異なるが最低でも8桁の整数を生
成
– VALUEプロシージャ(Oracle10g~)指定した引数の範囲内の数値を生成
範囲の指定がないと0以上1未満の精度38桁の小数を生
成
以下、上記パッケージの利用例です。DBMS_RANDOMを利用するには、$ORACLE_HOME/rdbms/admin/catoctk.sqlを実行してください。
DECLARE
I BINARY_INTEGER;
BEGIN
DBMS_RANDOM.INITIALIZE(92541);
FOR CNT IN 1..10
LOOP
DBMS_LOCK.SLEEP(10);
I := DBMS_RANDOM.RANDOM;
INSERT INTO SOME_TABLE VALUES(I);
COMMIT;
END LOOP;
DBMS_RANDOM.TERMINATE;
END;
/
Page-19
19
その他便利なパッケージ(2)DBMS_OBFUSCATION_TOOLKIT (Oracle8i~)
– データの暗号化、符号化
DBMS_CRYPTO(Oracle10g~)– データの暗号化、符号化
– DBMS_OBFUSCATION_TOOLKITより高機能
UTL_SMTP (Oracle8i~) ※8.1.6より
– メール送信、コーディングは非常に難しい
UTL_MAIL(Oracle10g~)– メール送信
– UTL_SMTPよりコーディングが簡単だが制限多し
Page-20
20
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-21
21
コード設計/開発のヒント(1)モジュール名・パラメータ名
– パラメータ・モード
独立したモジュール
– 再利用
パラメータ設計のヒント
– グローバル変数の利用に注意
– コメントを付ける
プロシージャ、関数、パラメータの使用についての効果的なPL/SQLプログラミングの方法をまとめます。
モジュール名・パラメータ名
•モジュールの動作内容の分かるモジュール名をつける。
•仮パラメータのパラメータ・モードを使いパラメータの種類が分かるようにする。ただし、ファンクションにはOUTパラメータは使わない。
独立したモジュール
•モジュールを作成するときには、現在のシステムとそれ以外のシステムでも再利用できるような書き方をする。
•コードの記述量が減り、バグの発生も開発時間も減少する。
パラメータ設計のヒント
•グローバル変数はできるだけ使わず、パラメータを使用する。
(モジュールのインターフェイスのみで使用方法が分かるように)
•パラメータと機能についてコメントをつける。
Page-22
22
コード設計/開発のヒント(2)
ファンクション(関数)の利用
– 汎用性のある関数を作る。
– 関数は1つ値を返すようにコーディングする
– OUTパラメータは使用しないようにする
– 例外処理
– 無効なパラメータのトラップ
ファンクション(関数)の利用
ファンクションは、式の一部としてコールされます。
IF sal_ok(new_sal, new_title) THEN ...
使用しやすさから、ファンクションは複数の値を返すようにコーディングすべきでは有りません。
----- 以下、関数の例 ---------------------------------------------FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN VARCHAR2
IS
return_value VARCHAR2(20) := NULL;
BEGIN
/* 有効なパラメータの検査 */
IF status_cd_in = 'C' THEN return_value := 'CLOSED';ELSIF status_cd_in = 'O' THEN return_value := 'OPEN';ELSE
RAISE value_error;
END IF;
RETURN return_value;
EXCEPTION
WHEN value_error THEN
RETURN NULL;
END;
Page-23
23
コード設計/開発のヒント(3)
ローカル・モジュールの活用
– コードの重複をなくす
ローカル・モジュール :
宣言部内に定義されたプロシージャ / ファンクション
有効範囲は宣言されているモジュール内
ローカル・モジュールの活用
以下の例では、実行部で繰り返し利用される処理をファンクションNPVとして、宣言部に定義しています。これによって処理の定義を繰り返し行わなくてすみます。
PROCEDURE format_data
(projected_sales_in IN NUMBER,year_in IN INTEGER)
IS
total_cost NUMBER(9);
gross_profit NUMBER(9);
crew_labor NUMBER(9);
mgmt_labor NUMBER(9);
/* ---------- ローカル・モジュール ----------*/
FUNCTION npv (column_in IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN
TO_CHAR((net_present_value ( '' || column_in, year_in) /
projected_sales_in *100),'999.99');END;
BEGIN
:owner.total_cost_pc := npv('total_cost');:owner.gross_profit_pc := npv('grew_profit');:owner.crew_labor_pc := npv('crew_labor');:owner.mgmt_labor_pc := npv('mgmt_labor');
END;
宣言部
実行部
Page-24
24
コード設計/開発のヒント(4)
LOOP、無名ブロックにラベルを付ける
DB内のレコードの存在チェック方法
<<ラベル>>
cursor%NOTFOUND
LOOP、無名ブロックにラベル
<<every_month>>FOR month IN 1..12 LOOP
END LOOP every_month;
<<sgn>>BEGIN
dbms_output.put_line('hello...');
END sgn;
レコードの有無の確認
DB内のレコードの存在チェック方法を調べるには'SELECT COUNT(*) ...'を使わずに、 カーソルを開いて1行FETCHしcursor%NOTFOUND.
declare
cursor c1 is select * from notfound;
not_rec c1%rowtype;
begin
open c1;
fetch c1 into not_rec;
if (c1%notfound) then
dbms_output.put_line('Nashi');
end if;
end;
/
Page-25
25
コード設計/開発のヒント(5)パッケージ化
– モジュール性
– トップダウンアプリケーション設計
– 情報隠蔽
– 永続的状態
– パフォーマンスの向上プロシージャ ファンクション
変数宣言
例外宣言カーソル宣言
定数宣言
モジュール性
理論的に関連するオブジェクトをまとめることができ、モジュール管理がやりやすくなります。
トップダウンアプリケーション設計
パッケージ内の手続き部分のコーディング以前に、呼び出し方法のみを登録できるため、開発生産性が向上します。
情報隠蔽
外部から使用できる要素(パブリック・プロシージャ、グローバル変数)と、パッケージ内でのみ使用できる要素(プライベート・プロシージャ、ローカル変数)を区別でき、モジュールのセキュリティを高めることが出来ます。
永続的状態
・単一Oracleセッション内でグローバル性を維持可能
・変数やカーソルはセッションを通じてその内容を保持可能
パフォーマンスの向上パッケージ内の要素が呼び出されると、パッケージ全体がメモリにロードされるため、よく呼び出されるモジュールをまとめると呼び出し速度が向上します。
Page-26
26
コード設計/開発のヒント(6)
変数の宣言
– %TYPE
– %ROWTYPE
my_empno emp.empno%TYPE;
emp_rec emp%ROWTYPE;
cursor c1 is select * from dept;dept_rec c1%ROWTYPE;
変数の宣言 ( %TYPE、%ROWTYPE )<<%TYPE>>
データベースの要素が変わっても、動作可能なモジュールが作成できます。(生成時にデータベースを参照、データベースの要素が変更されると自動的に再コンパイルされます)
また、正確なデータ型を知らなくてすみます。
上の例では、my_empno をemp表のempnoカラムと同じデータ型で扱います 。
<<%ROWTYPE>>
上の一番目の例では、emp表から選択された行が格納できます。例えば、以下のように使用します。
ドット表記で各フィールドを参照できます。
DECLARE
emp_rec emp%ROWTYPE;
BEGIN
select * into emp_rec from emp where ...;
emp_req.sal = emp_req.sal + 10;
...
2番目の例は、カーソルc1で取り出された行を格納できます。
Page-27
27
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-28
28
コレクション
PL/SQL表 Vs. Nested Table & Varray
Nested Table と Varray
PL/SQL内、データベース内PL/SQL内
Nested Table & VarrayPL/SQL表
必要なし必要あり要素数の制限の指定
なし
Nested Table
あり順序付け
Varray
PL/SQL リリース2(Oracle7)までは、PL/SQL表という一次元の配列をサポートしていました。PL/SQL リリース8.0(Oracle8)からはNested TableとVarrayをサポートします。
VARRAYは要素の数の制限を指定する必要があります。Nested Tableは上限がないため各オブジェクトの変更が自由に行えます。VARRAYとNested Tableは、PL/SQL内で利用する場合には他に違いは有りません。
Page-29
declaretype color_tab_type is table of varchar2(10)
index by binary_integer;color_tab color_tab_type;
begincolor_tab(1) := 'Black';color_tab(2) := 'White';color_tab(3) := 'Green';color_tab(4) := 'Red';
for i in 1..color_tab.countloop
dbms_output.put_line(color_tab(i));end loop;
end;/
declaretype Color_tab_t is table of varchar2(10);colors Color_tab_t := Color_tab_t();
begincolors.extend(4);colors(1) := 'Blue';colors(2) := 'Yellow';colors(3) := 'Gold';colors(4) := 'Silver';
for i in 1..colors.countloop
dbms_output.put_line(colors(i));end loop;
end;/
declaretype Color_varray_t is varray(16) of varchar2(10);colors Color_varray_t := Color_varray_t();
begincolors.extend(4);colors(1) := 'Pink';colors(2) := 'Purple';colors(3) := 'Gray';colors(4) := 'Orange';
for i in 1..colors.countloop
dbms_output.put_line(colors(i));end loop;
end;/
PL/SQL表
Nested Table
Varray
Page-30
30
ラージ・オブジェクト(LOB)
内部LOB … データベース表領域内に格納
BLOB - バイナリファイル
CLOB - 1バイト固定幅の文字データ
NCLOB -マルチバイト固定幅の文字データ
外部LOB … 表領域外に格納、OS ファイル
BFILE - バイナリ・ファイル
Oracle8からLOB(4GB(10gだとブロックサイズにより8~128TB)までのRAWまたはバイナリ・データ、文字データ)を格納できるデータ型、SQL DDL/DMLコマンド、OCI/PLSQLのAPIを提供しています。LONG/LONG RAW型は下位互換性の為に残されていますが、新規にデータベースを作成される際にはLOB型を使用するようにして下さい。
• LOBはレプリケーションすることも可能(BFILEを除く)
• 一つの表に複数のカラムを持つことも可能です。
• 格納方法もOracleの内部はもちろん、外部にも格納することができます。
• データベース・バッファを有効に利用するために、LOB型データをバッファリングするかどうかの指定をすることが可能です。
• REDOログへのロギングも行うかどうかの指定ができます。
DBMS_LOB
APPEND LOB値を別のLOBに追加
COPY LOBの一部を別のLOBにコピー
ERASE 指定のオフセットから開始して、LOBの一部を消去
LOADFROMFILE BFILEデータを内部LOBにロード
WRITE 指定されたオフセットからLOBにデータを書き込む
GETLENGTH LOB値の長さを取得
READ指定されたオフセットからLOBデータを読み込む
FILEOPEN ファイルをオープン
FILECLOSE ファイルをクローズ
OPEN LOBをオープン
CLOSE LOBをクローズ
Page-31
BLOB,BFILE 格納と長さの取得
以下のサンプルでは、
1. BLOB、BFILEの各カラムにファイル(/tmp/aaa)の内容を挿入します。
2. DB内の、BLOB、BFILEデータを読み込み内容の長さを取得、表示します。
CREATE TABLE TEMP( KEY INTEGER, IMG_IN_DB BLOB, IMG_OUT_DB BFILE );
CREATE DIRECTORY HDOC AS '/TMP';
DECLARE LOBD BLOB; FILS BFILE := BFILENAME('HDOC', 'aaa'); AMT INTEGER;
BEGIN/* ファイルを読込み専用でオープン */
DBMS_LOB.FILEOPEN(FILS, DBMS_LOB.FILE_READONLY);
/* ファイルの長さを取得 */AMT:=DBMS_LOB.GETLENGTH(FILS);
/* BLOBカラムを初期化、空のBLOB値と共に1行INSERT、ロケータを返す */INSERT INTO TEMP VALUES (1, EMPTY_BLOB(), FILS)
RETURNING IMG_IN_DB INTO LOBD;
/* LOB値に実データを挿入 */DBMS_LOB.LOADFROMFILE(LOBD, FILS, AMT);COMMIT;
/* ファイルをクローズ */DBMS_LOB.FILECLOSE(FILS);
/* BFILE , BLOBに格納された内容を読込、出力 */SELECT IMG_IN_DB, IMG_OUT_DB INTO LOBD, FILS FROM TEMP;AMT:=DBMS_LOB.GETLENGTH(FILS); DBMS_OUTPUT.PUT_LINE(‘BFILE: ’||AMT); AMT:=DBMS_LOB.GETLENGTH(LOBD); DBMS_OUTPUT.PUT_LINE(‘BLOB: ’||AMT);
END; /
実行結果:
BFILE: 9
BLOB: 9
Page-32
CLOB 格納と操作
以下のサンプルでは、
1. CLOBカラムに文字列(‘Character Lob’)を挿入します。
2. DB内の、CLOBデータを2文字ずつ読み込み、表示します。
CREATE TABLE TEST (A NUMBER,B CLOB);INSERT INTO TEST VALUES (1,'Character Lob');COMMIT;SELECT A,DBMS_LOB.GETLENGTH(B) FROM TEST;
実行結果:A DBMS_LOB.GETLENGTH(B)
--------- ---------------------1 13
DECLARELOB_LOC CLOB;BUFFER VARCHAR2(2); /* 読込バッファ*/AMOUNT BINARY_INTEGER := 2;LENGTH BINARY_INTEGER;POSITION INTEGER := 1;
BEGINSELECT B INTO LOB_LOC FROM TEST;DBMS_LOB.OPEN(LOB_LOC, DBMS_LOB.LOB_READONLY);
/* CLOBの内容の長さを入手 */LENGTH := DBMS_LOB.GETLENGTH(LOB_LOC);
/* オフセットを2文字ずつ進め、文字をバッファに読み込む */WHILE POSITION <= LENGTH LOOP
DBMS_LOB.READ (LOB_LOC, AMOUNT, POSITION, BUFFER); POSITION := POSITION + AMOUNT;DBMS_OUTPUT.PUT_LINE(BUFFER);
END LOOP;
DBMS_LOB.CLOSE(LOB_LOC);END;/
実行結果:
Ch
ar
ac
te
r
Lo
b
Page-33
33
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-34
34
ラッピング
PL/SQL ラッパー
– PL/SQLソースコードをオブジェクトコードの中間形式に
変換する
– アプリケーションの内部を隠す
C:¥> WRAP INAME=input_file [ONAME=output_file]
PL/SQL ラッパーを使用すると、PL/SQLソースコードをオブジェクトコードの中間形式に変換します。
ディクショナリ内でも、ラップ化されます。(USER_SOURCE等)
利点
•コードを隠して、アプリケーションの公開
•ソースコードと同等の移植性(動的ローディング、EXP/IMPなど)
CREATE OR REPLACE PROCEDURE get_nextline(file_in IN UTL_FILE.FILE_TYPE,line_out OUT VARCHAR2,eof_out OUT BOOLEAN)
ISBEGIN
UTL_FILE.GET_LINE (file_in, line_out);eof_out := FALSE;
EXCEPTIONWHEN NO_DATA_FOUNDTHEN
line_out := NULL;eof_out := TRUE;
END;
ラップ前
CREATE OR REPLACE PROCEDURE get_nextline wrapped0abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd。。
ラップ後
Page-35
35
DB内のコード(1)
USER_OBJECTS– PL/SQLオブジェクトの状態を知る
OBJECT_TYPE OBJECT_NAME STATUS
-------------------- ------------------------------ ----------
PACKAGE DBMS_REPCAT_AUTH VALID
PACKAGE BODY DBMS_REPCAT_AUTH VALID
PROCEDURE ORA$_SYS_REP_AUTH VALID
USER_OBJECTS:ユーザーが所有するオブジェクト情報
モジュール内で参照されている表・他のプログラムが変更されるとそのモジュールの状態(STATUS)がINVALIDになります。INVALID状態のモジュールは実行されるときに自動的に再コンパイルされます。その間、ユーザーは待ち状態となってしまいます。それを回避する為に手動で再コンパイルしておく必要が有ります。
以下のスクリプトをSQL*Plusで実行すると上のような結果が得られます。
SET PAGESIZE 66COLUMN object_type FORMAT A20COLUMN object_name FORMAT A30COLUMN status FORMAT A10BREAK ON object_type SKIP 1SPOOL psobj.lisSELECT object_type, object_name, statusFROM user_objectsWHERE object_type IN ('PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'PROCEDURE')ORDER BY object_type, status, object_name/SPOOL OFF
Page-36
36
DB内のコード(2)
USER_OBJECT_SIZE– PL/SQLコードサイズの分析
火 May 25
ページ 1
Size of PL/SQL Objects > 2000 Bytes
NAME TYPE SOURCE_SIZE PARSED_SIZE CODE_SIZE
------------------ -------------- ----------- ----------- ---------
DBMS_REPCAT_AUTH PACKAGE BODY 10712 0 3130
USER_OBJECT_SIZE:ユーザーが所有するオブジェクトのサイズ
ストアド・オブジェクトのコンパイル済みのコードは、DBのSGAにロードされ実行されるので、あまりに大きなプログラムは避けるべきです。(大きなプログラムをSGAに確保するとそれ以外のプログラムがSGAから排除されてしまい、次回の実行時にロードし直さなくてはならない為)
以下のスクリプトをSQL*Plusで実行すると、上のように比較的大きなモジュールを検索できます。
SET PAGESIZE 66COLUMN name FORMAT A30COLUMN type FORMAT A15COLUMN source_size FORMAT 999999COLUMN parsed_size FORMAT 999999COLUMN code_size FORMAT 999999TTITLE 'Size of PL/SQL Objects > 2000 Bytes'SPOOL pssize.lisSELECT name, type, source_size, parsed_size, code_sizeFROM user_object_sizeWHERE code_size > 2000ORDER BY code_size DESC/SPOOL OFF
Page-37
37
DB内のコード(3)
USER_SOURCE– ソース・コードの相互参照
– 行番号でソースを検索
LINE TEXT---------- --------------------------------------------------
1 PROCEDURE get_nextline2 (file_in IN UTL_FILE.FILE_TYPE,3 line_out OUT VARCHAR2,4 eof_out OUT BOOLEAN)5 IS6 BEGIN
USER_SOURCE:作成したオブジェクトのソース
以下のスクリプトをSQL*Plusで実行すると上の例のように、ソースが参照できます。
これによって、PL/SQLが表示するエラーの発生行を特定することができます。
column text format a50select line,text from user_source
where name=UPPER(‘&1’) /* オブジェクトの名前 */and type = 'PROCEDURE' /* オブジェクトのタイプ */order by line;
Page-38
38
DB内のコード(4)
USER_DEPENDENCIES– オブジェクトの依存関係を参照する
FROM_ TO_ TO_TYPE TO_OWNER TO_LINK
------------ ------------ ------------ ------------ ------------
PROC2 PROC1 PROCEDURE SCOTT
PROC3 PROC2 PROCEDURE SCOTT
PTEST2 PTEST1 PROCEDURE SCOTT
USER_DEPENDENCIES:ユーザーが所有するオブジェクトの依存関係
以下のスクリプトをSQL*Plusで実行すると上の例のように、SCOTTユーザーのプロシージャを参照しているオブジェクトが表示されます。(TO_ に表示されるオブジェクトを変更するとFROM_ に表示されているPL/SQLオブジェクトが影響を受けINVALIDになる)
column From_ format a12column To_ format a12column To_TYPE format a12column To_OWNER format a12column To_LINK format a12
SELECT name From_,referenced_name To_,referenced_type To_type,referenced_owner To_owner,referenced_link_name To_linkFROM user_dependenciesWHERE referenced_owner = UPPER ('scott')AND referenced_type = UPPER ('procedure');
Page-39
39
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-40
40
コンパイル時のエラー表示
SHOW ERRORS (SQL*Plusコマンド)
– コンパイル時のエラーを表示する
警告: プロシージャが作成されましたが、コンパイル・エラーがあります。
プロシージャ、パッケージのコンパイル時のエラーはSQL*Plusを用いている場合、SHOW ERRORSコマンドを使用して表示することができます。
ここでのエラーの行番号は、USER_SOURCE(参照:Page37)で確認してください。
SQL> SHOW ERRORS PROCEDURE ANCHOR_TST
PROCEDURE ANCHOR_TSTのエラーです。
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/10 PLS-00201: 識別子: PLS_ANCHOR_TEST.AAAを宣言してください。
3/10 PL/SQL: Item ignored
4/16 PL/SQL: SQL Statement ignored
4/32 PLS-00201: 識別子: PLS_ANCHOR_TESTを宣言してください。
7/3 PL/SQL: SQL Statement ignored
7/17 PLS-00320: この式の型の宣言が不完全か、または形式が誤っています。
8/3 PL/SQL: Statement ignored
8/24 PLS-00320: この式の型の宣言が不完全か、または形式が誤っています。
Page-41
41
実行時の変数、式の値
標準出力– DBMS_OUTPUTパッケージ
ダミー表に挿入– Insert + Commit、自律型トランザクション
PIPE– DBMS_PIPEパッケージ
外部ファイル(OSファイル)に出力
– UTL_FILEパッケージ
DBMS_OUTPUTのPUT文、PUT_LINE文を使用して、変数および式の値を端末に出力できます。
パッケージなどを作成して、使いやすいように改良。 簡単な入力ですむようになど
以下、利用方法と内容が確認できるタイミングの違いについてまとめました。
DBMS_OUTPUT (標準出力) (参照: Page7)実行後にまとめて出力されます。以下の例では、約10秒後に全結果が出力されます。
set serveroutput on
declaretemp_no pls_integer :=1;
beginfor i in 1..10 loopdbms_output.put_line('Temp No is ' ||temp_no);dbms_lock.sleep(1);temp_no := temp_no+1;
end loop;end;/
Page-42
ダミー表
変数の値を格納する為の一時表を作成しておき、INSERT文を組み込み値を挿入します。Commit を発行した時点で参照が可能になるので、実処理に影響が出る恐れがありますので注意が必要です。
…for i in 1..10 loop
insert into temp values(temp_no);commit;dbms_lock.sleep(1);temp_no := temp_no+1;
end loop;…
PIPEDBMS_PIPEパッケージを使用しPIPE内にメッセージとして、変数の値を送信します。他セッションで値の確認を行います。(参照:Page12,13)要求を出した時点での、参照が可能です。
…for i in 1..10 loopdbms_pipe.pack_message('Temp No is '||temp_no);pstat := dbms_pipe.send_message('debug',10);dbms_lock.sleep(1);temp_no := temp_no+1;
end loop;…
外部ファイル
UTL_FILEパッケージを使用します。(参照:Page8,9)実行ログとしても利用が可能です。
UTL_FILE.FFLUSHを行えば逐次の参照が可能です。以下の例では、毎行の情報を外部ファイルから取得することができます。(例えばUNIXコマンド“tail”など)
…fhandle := utl_file.fopen('/tmp','debug.log','w');for i in 1..10 looputl_file.put_line(fhandle,'Temp No is ' ||temp_no);utl_file.fflush(fhandle);dbms_lock.sleep(1);temp_no := temp_no+1;
end loop;…
Page-43
43
JDeveloperを使用したデバッグ
高度なデバッグ機能
– ステップ実行、トレース実行
– 変数の内容の監視、動的な変更
Visual Studio等の
統合開発環境のようなデバッグ作業を実現
JDeveloperはJava専用の統合開発ツールと捕らえられがちですが、PL/SQLの統合開発ツールでもあります。
JDeveloperを使用することにより、PL/SQLにおいてもJavaや.Netと同様の統合開発環境が得られます。
もちろんデバッグについてもJavaと同様に行うことが可能です。
JDeveloperによるPL/SQLアプリケーションの開発方法の概要については以下をご参照ください。
http://otndnld.oracle.co.jp/products/jdev/htdocs/db_overview/db_overview.html
Page-44
44
コール・スタック情報
コール・スタックの取得– DBMS_UTILITY.FORMAT_CALL_STACK
Procedureproc1
実行
Procedureproc2
Procedureproc3
呼び出し
呼び出し
DBMS_UTILITYのファンクションを使用して、現在のコール・スタック及びエラー・スタックを取得できます。
例えば、
dbms_output.put_line(dbms_utility.format_call_stack);
を、エラーの発生する直前に、挿入しておけば、エラー発生時のコール・スタック情報が取得できます。
以下の例では、上の図のような関係のプロシージャのうち、プロシージャproc1の中でコールスタックを取得した結果です。
呼び出したモジュールと、その行数が分かります。
----- PL/SQL Call Stack -----
object line object
handle number
name
809612c8 4 procedure YHAYASHI.PROC1
80960268 4 procedure YHAYASHI.PROC2
8095c77c 4 procedure YHAYASHI.PROC3
808b6318 2 anonymous block
Page-45
45
エラー・スタック情報
エラー・スタックの取得– DBMS_UTILITY.FORMAT_ERROR_STACK
ttt_insert
表:TTT トリガー
プロシージャ:A
プロシージャ:B
実行
実行
INSERT
(失敗)
例外発生
dbms_output.put_line(dbms_utility.format_error_stack);
を例外処理の中で使用すれば発生した一連のエラー・スタックが入手できます。
下記サンプルでは、トリガー内でのエラー情報がプロシージャ(B)内の例外処理で取得できています。プロシージャ(A)の処理は例外処理を行わないのでエラー情報は取得していません。
CREATE TABLE ttt (f1 number);
CREATE OR REPLACE TRIGGER ttt_insert
BEFORE INSERT ON ttt
BEGIN
RAISE ZERO_DIVIDE;
END ttt_insert;
/
(次ページへ続く)
例外を発生させる
Page-46
CREATE OR REPLACE PROCEDURE B AS
BEGIN
INSERT INTO ttt VALUES (7);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('PROC B: '||dbms_utility.format_error_stack ||'##');
END B;
/
CREATE OR REPLACE PROCEDURE A AS
BEGIN
B;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('PROC A: '||dbms_utility.format_error_stack ||'##');
END A;
/
set serveroutput on
exec a;
PROC B: ORA-01476: 除数がゼロです。ORA-06512: "YHAYASHI.TTT_INSERT", 行:2ORA-04088: トリガー: YHAYASHI.TTT_INSERTの実行中にエラーが発生しました。##
---------------- 以下、実行結果 ----------------------
Page-47
47
Agenda
PL/SQLとは
組み込みパッケージ
コード設計/開発のヒント
その他TIPSコードの管理
PL/SQLのデバッグ
PL/SQLのチューニング
Page-48
48
パフォーマンス分析~プロファイリング~
プロファイリングとは?
– プログラムの稼動情報を収集し、分析すること
実行時間
CPU時間
メモリ消費
– パフォーマンス・ボトルネックの調査などに利用
PL/SQLアプリのプロファイリング
– DBMS_PROIFILERパッケージを利用
各行の実行時間及び実行回数のみ取得可
Oracle8i~
DBMS_PROIFILERパッケージを利用することにより、各行の実行時間を計ることが可能になります。DBMS_PROIFILERを使用する事前準備として、以下の作業が必要になります。
1.SYSユーザーで$ORACLE_HOME/rdbms/admin/profload.sqlを実行してDBMS_PROIFILERをインストール
2. パッケージ実行ユーザーで$ORACLE_HOME/rdbms/admin/proftab.sqlを実行してDBMS_PROIFILERが使用するテーブルを作成
DBMS_PROFILERパッケージは以下の様にして使用します。
1. START_PROFILERプロシージャを実行してプロファイリング開始
SQL> execute dbms_profiler.start_profiler ('PROFILING SAMPLE1')
2.プロファイリング対象アプリを実行
3. STOP_PROFILERプロシージャを実行してプロファイリング終了
SQL> execute dbms_profiler.stop_profiler
4.プロファイリング情報テーブルを検索
(1)PLSQL_PROFILER_RUNS
プロファイリング対象アプリの実行ID(RUNID列)を確認
(2)PLSQL_PROFILER_DATA
プロファイル内容を確認
Page-49
4--(2)を確認する場合、以下のSQLを流用して下さい。
select p.unit_name, p.occured, p.tot_time, p.line# line,substr(s.text, 1,75) text
from (select u.unit_name, d.TOTAL_OCCUR occured,(d.TOTAL_TIME/1000000000) tot_time, d.line#from plsql_profiler_units u, plsql_profiler_data dwhere d.RUNID=u.runid and d.UNIT_NUMBER = u.unit_number
and d.TOTAL_OCCUR >0and u.runid = ※) p, user_source s
where p.unit_name = s.name(+) and p.line# = s.line (+)order by p.unit_name, p.line#;
このSQLの下から3行目の「※」の部分を、4-(1)で確認したRUNID列の値にして下さい。
このSQLを実行すると、例えば以下の様に表示されます。TOT_TIME列が実行時間で、単位はナノ秒です。
UNIT_NAME OCCURED TOT_TIME LINE TEXT--------------- ------- ----------- ---------- -----------------------------------NUMBER_TEST 1 .002074 2 n number := 1;NUMBER_TEST 1 .000388 3 b binary_integer := 1;NUMBER_TEST 1 .000068 4 p pls_integer := 1;NUMBER_TEST 1 .001185 7 dummy := 'start of plsql';NUMBER_TEST 101 .030679 8 for i in 1 .. 100 loopNUMBER_TEST 100 .027835 9 n := n + 1;NUMBER_TEST 100 .046406 10 b := b + 1;NUMBER_TEST 100 .009543 11 p := p + 1;NUMBER_TEST 100 .014314 12 dummy := 'counts for endloop';NUMBER_TEST 1 .001272 14 dummy := 'end of plsql';
Page-50
50
モジュールアクセススピード向上
重要なコードはSGAに固定
– DBMS_SHARED_POOL.KEEP : 固定する
– DBMS_SHARED_POOL.UNKEEP : 解放する
自動再コンパイルを避ける
クライアント側SQLを避ける
重要なコードはSGAに固定する
ストアド・モジュールを実行する際には、コンパイル済みのコードがSGAにロードされている必要が有ります。すでにSGAにロードされていればその分、実行時間は速くなります。もっとも使用するパッケージは常にメモリ上に有るようにすると効果的です。
KEEP実行時にはロードされず、パッケージ実行時に実際はロードされます。DB起動直後のSGAが断片化していないときにパッケージの固定を行うことが推奨されています。
他のリソースの使用量も考えて固定するパッケージについて検討する必要が有ります。
例えば、以下のようなパッケージの固定が考えられます。
STANDARD、DBMS_STANDARD、DBMS_UTILITY、DBMS_DESCRIBE、DBMS_OUTPUT、DBMS_LOCK、DBMS_PIPE
自動再コンパイルを避ける。
参照:Page 35クライアント側SQLを避ける
同じSQLコードが複数のプログラムで共有しやすくなり、解析済みの共有SQLコードの利用率が高まります。
プロシージャや関数にまとめることで、ネットワークに1回アクセスするだけで一連の操作が実行できます。
Page-51
51
アルゴリズム
ローカル変数の使用
PLS_INTEGER
NOT NULL制約を避ける
ローカル変数の使用
パラメータ値をローカルコピーを作成しコピーします。
これにより、以下の左の例では繰り返しUPPER関数を呼び出しているのに対して、右の例では1回の呼び出しですみます。
PROCEDURE calc_sales
(company_id IN NUMBER, action_in IN VARCHAR2)
IS
BEGIN
IF UPPER (action_in) = 'ANNUAL' THEN ...
ELSIF UPPER (action_in) = 'QUARTERLY' THEN ...
ELSIF UPPER (action_in) = ...
END IF;
END;
PROCEDURE calc_sales
(company_id IN NUMBER, action_in IN VARCHAR2)
IS
action_int VARCHAR2(10) := UPPER(action_in);
BEGIN
IF action_int = 'ANNUAL' THEN ...
ELSIF action_int = 'QUARTERLY' THEN ...
ELSIF action_int = ...
END IF;
END;
Page-52
PLS_INTEGERデータ型
符号付き整数(-2147483647 ~ 2147483647)の操作にはPLS_INTEGERデータ型の使用が一番効率が良い。(記憶領域、処理速度の点で)
NOT NULL制約を避ける
PL/SQL内の変数にNOT NULL制約をつけると、変数の代入毎に一時変数を使い値の検証を行ってしまいます。避ける為にはプログラミングで制約を実現します。
PROCEDURE foo IS
m number;
BEGIN
m := a+b;
m := m * 1.2;
m := m * m;
if (m is null) then
----- エラー処理 -----
end if;
...
END;
PROCEDURE foo IS
m number NOT NULL;
BEGIN
m := a+b;
m := m * 1.2;
m := m * m;
...
END;
Page-53
53
日本オラクル株式会社Copyright ©2005, Oracle. All rights reserved.
このドキュメントは単に情報として提供され、内容は予告なしに変更される場合があります。このドキュメントに誤りが無いことの保証や、商品性又は特定目的への適合性の黙示的な保証や条件を含め明示的又は黙示的な保証や条件は一切無いものとします。オラクル社は、このドキュメントについていかなる責任も負いません。また、このドキュメントによって直接又は間接にいかなる契約上の義務も負うものではありません。このドキュメントを形式、手段(電子的又は機械的)、目的に関係なく、オラクル社の書面による事前の承諾なく、複製又は転載することはできません。
OracleはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、各社の商標ま
たは登録商標です。
top related