...

PL/SQL - OTN

by user

on
Category: Documents
152

views

Report

Comments

Transcript

PL/SQL - OTN
PL/SQL
コーディング TIPS
第1版
1999/06/29
日本オラクル株式会社
この資料では、PL/SQLの概要、コーディング上便利な機能・注意すべき点・コツ
を中心にご説明させていただきます。
この資料の内容は、PL/SQL リリース8.0.5での機能を基に記述しております。
1
38
2
目次

PL/SQLとは
とは

組み込みパッケージ
コード設計/開発のヒント
コード設計
開発のヒント

その他TIPS
その他


コードの管理
PL/SQLのデバッグ
のデバッグ

PL/SQLのチューニング
のチューニング

2
PL/SQLとは
PL/SQLとは
Procedual Language / Structured Query Language
(SQLへの手続き型言語拡張)
への手続き型言語拡張)
アプリケーション
begin
SQL文
SQL文
if
SQL文
SQL文
else
SQL文
SQL文
end if;
end;
PL/SQLは、SQL言語(標準的リレーショナル・データベース言語)には無いプロ
グラミング構造を提供します。
SQL言語を手続き型言語として機能拡張したものです。
3
PL/SQLとは
PL/SQLとは
- 利点
z
プロシージャ機能
z
エラー処理
z
パフォーマンスの向上
z
z
高い移植性
Oracleとの統合
との統合
プロシージャ機能
• 変数、定数の利用
エラー処理
• エラー(例外)を補足、処理が行える
パフォーマンスの向上
• 複数のSQLを一度にOracleに送信(特にネットワーク環境では効果有り)
高い移植性
• Oracleの稼働環境すべてに移行可能(Platformに依存しない)
Oracleとの統合
Oracleとの統合
• すべてのSQLデータ型をサポート
• データベース列の定義を基にした変数の宣言
4
組み込みパッケージ
z
DBMS_OUTPUT
z
UTL_FILE
z
DBMS_SQL
z
DBMS_PIPE
z
DBMS_ALERT
z
DBMS_JOB
z
DBMS_UTILITY
z
その他
代表的な組み込みパッケージについて、その用途とサンプルをご紹介させてい
ただきます。
5
組み込みパッケージ
- DBMS_OUTPUT
値、メッセージを表示します。
z
PUT
バッファに情報を書き込む(改行無)
PUT_LINE
バッファに情報を書き込む(改行有)
GET_LINE
バッファから1
バッファから1行を受け取る
NEW_LINE
バッファに改行を挿入
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プロシージャが正常に完了しました。
PL/SQLプロシージャが正常に完了しました。
参照:『PL/SQL
ユーザーズ・ガイドおよびリファレンス リリース8.0』
参照:『
リリース 』 (A56827-1) 8章
章-16
6
組み込みパッケージ
- UTL_FILE
z
OSのテキスト・ファイルの読み書き
のテキスト・ファイルの読み書き
Server
Client
Database
Text File
プログラムのインプットとアウトプットとして、サーバOS上のテキストファイルを使用する
ことで、PL/SQLプログラム以外との情報の受け渡しが可能になります。
テキストファイルのオープン、クローズ、リード、ライトの機能を提供します。
安全を保証する為に、ファイルへアクセスするには、初期化パラメータファイル
(init.ora)のUTL_FILE_DIRパラメータににディレクトリを指定しておく必要が有ります。
以下の手順で実行します。
ファイル入出力の手順
① ファイルをOPENする。 --- FOPEN
② ファイル操作。
ファイルから読み込む --- GET_LINE
ファイルに書き出す --- PUT, PUT_LINE, PUTF, NEW_LINE
③ ファイルをCLOSEする。 --- FCLOSE, FCLOSE_ALL
参照:『アプリケーション開発者ガイド リリース8.0』
章-27
リリース 』 (A56822-1) - 12章
7
以下、ディレクトリ(c:¥temp)にファイル(aaa.txt)を作成し、1行書き出し、その内容を読み込
み出力するサンプルです。
INIT.ORA(
(UTL_FILE_DIR)
):
INIT.ORA に以下のようにディレクトリを指定(絶対パス)。大文字/小文字の指定まで正し
いことを確認する。
UTL_FILE_DIR = *
または、
UTL_FILE_DIR = c:\
c:\temp
fopenプロシージャで指定するディレクトリの書き方と全く同じでなくては有効になりません。
(PATHの最後に‘/’または‘¥’を付けるなど)
set serveroutput on
DECLARE
filehndl UTL_FILE.FILE_TYPE;
filedata varchar2(100);
BEGIN
/* 書き込みモードでOPEN
書き込みモードでOPEN */
filehndl := UTL_FILE.FOPEN('c:\
UTL_FILE.FOPEN('c:\temp','aaa.txt','w');
/* ファイルに
ファイルにHello
Hello Worldと出力
Worldと出力 */
UTL_FILE.PUT_LINE(filehndl,'Hello World');
UTL_FILE.FCLOSE(filehndl);
/* 読込モードで
読込モードでOPEN
OPEN */
filehndl := UTL_FILE.FOPEN('c:\
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に依存します。
(例)UNIXサーバ、データベース・キャラクタセット JA16EUCの場合
• 日本語EUCの改行コード(0x0A)
8
組み込みパッケージ
- DBMS_SQL
z
動的SQLの実行
の実行
動的
–
z
DDL文、動的に条件の変わる
文、動的に条件の変わるSQL文
文
文、動的に条件の変わる
プログラムが汎用的になる。
動的SQLとは、コンパイル時は未完成(条件等が決まっていない)ようなDML文や、
DDL文のことを表わします。
実行時に、条件などが指定できるのでプログラムに汎用性を持たせることができます。
処理の手順は以下のようになります。
<<処理手順>>
① カーソルOPEN (DBMS_SQL.OPEN_CURSOR)
② SQL文の解析 (DBMS_SQL.PARSE)
③ EXECUTEのコール (DBMS_SQL.EXECUTE)
④ カーソルのクローズ (DBMS_SQL.CLOSE_CURSOR)
DBMS_SQLパッケージ・ファンクション(一部)
パッケージ・ファンクション(一部)
BIND_VARIABLE
所定の値を所定の変数にバインド。
DEFINE_COLUMN
指定されたカーソルから選択され、SELECT文だ
けで使用される列を定義します。
COLUMN_VALUE
所定のカーソルの所定の位置のカーソル要素の
値を戻す。
FETCH_ROWS
所定のカーソルから行をフェッチ。
IS_OPEN
指定されたカーソルがオープンならTRUE
参照:『アプリケーション開発者ガイド リリース8.0』
章
リリース 』 (A56822-1) - 14章
9
以下、指定された番号よりも大きな従業員番号を持つ従業員を、EMP表から検
索するサンプルです。
CREATE or REPLACE PROCEDURE rows_greater_than (low_value number) AS
cursor1
integer;
rows_processed
integer;
myempno number;
myename varchar2(20);
BEGIN
①
②
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse (cursor1, 'select empno, ename from emp where empno >
:x',dbms_sql.v7);
dbms_sql.bind_variable(cursor1, 'x', low_value);
dbms_sql.define_column (cursor1, 1, myempno);
dbms_sql.define_column (cursor1, 2, myename, 20);
rows_processed := dbms_sql.execute (cursor1);
③
loop
if dbms_sql.fetch_rows (cursor1) > 0 then
dbms_sql.column_value (cursor1, 1, myempno);
dbms_sql.column_value (cursor1, 2, myename);
dbms_output.put_line(to_char(myempno) || '
' || myename);
else
exit;
end if;
end loop;
dbms_sql.close_cursor (cursor1);
④
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
if dbms_sql.is_open (cursor1) then
dbms_sql.close_cursor (cursor1);
end if;
END;
/
----------- 以下、実行結果 ----------SQL> set serveroutput on
SQL> execute rows_greater_than(7900);
7902
FORD
7934
MILLER
PL/SQLプロシージャが正常に完了しました。
PL/SQLプロシージャが正常に完了しました。
10
組み込みパッケージ
- DBMS_PIPE
z
パイプ(SGA)を使ったセッション間通信
)を使ったセッション間通信
パイプ(
Database
Oracle SGA
メッセージ送信
セッション
メッセージ受信
セッション
同一インスタンス内のプログラムでお互いにメッセージのやり取りができます。
非同期に、いつでもメッセージを受け取ることが可能です。
SGAにメッセージ情報を記録しますので、SHUTDOWNを行うと消えてしまいま
す。
DBMS_PIPEパッケージ・ファンクション(一部)
パッケージ・ファンクション(一部)
PACK_MESSAGE(メッセージアイテム)
ローカル・バッファにメッセージを作成
SEND_MESSAGE(パイプ名,タイムアウト,最大パイプサイズ)
名前付きパイプにメッセージを送信。名前付きパイプが存在しな
い場合には、パブリック・パイプが暗黙的に作成される。
RECEIVE_MESSAGE(パイプ名,タイムアウト)
名前付きパイプからローカル・バッファへメッセージをコピー。
UNPACK_MESSAGE(メッセージアイテム)
バッファ内の次の項目へアクセス。
PURGE(パイプ名)
名前付きパイプの内容を削除。
参照:『アプリケーション開発者ガイド リリース8.0』
章
リリース 』 (A56822-1) - 12章
11
以下、PIPEを使用した簡単なサンプルプログラムです。
SQL*Plusを2つ立ち上げ、接続します。(ユーザーは同じでなくても可)
一方を<メッセージ送信セッション>、もう片方を<メッセージ受信セッション>として
以下を実行してください。
<メッセージ送信セッション>
メッセージ送信セッション>
declare
pipe_stat integer;
begin
/*項目をメッセージ・バッファに詰め込む
/*項目をメッセージ・バッファに詰め込む*/
項目をメッセージ・バッファに詰め込む*/
DBMS_PIPE.PACK_MESSAGE('HELLO!!');
/* メッセージ・バッファの内容をパイプに送信 */
PIPE
pipe_stat :=DBMS_PIPE.SEND_MESSAGE('pipe1',10);
end;
/
<メッセージ受信セッション>
メッセージ受信セッション>
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;
/
実際の利用例としては、ストアド・プロシージャ等から送信されたメッセージをPro*C
プログラムで受信し、OS上のコマンドを起動するといったことが考えられます。
参照:『アプリケーション開発者ガイド リリース8.0』
章-14 例-2
リリース 』 (A56822-1) - 12章
表を使って、メッセージのやり取りを行う方法も考えられますが、送信時に
COMMITを発行しなければならないので、他の処理に影響が及ぶ可能性が有る
という点に注意する必要が有ります。PIPEではその必要は有りません。
12
組み込みパッケージ
- DBMS_ALERT
アプリケーションB
アプリケーションB
(WAIT中
WAIT中)
アプリケーションA
アプリケーションA
更新
Oracle
Database
更新の通知
EMP表
EMP表
トリガー
EMP_TRIG
DBMS_ALERTパッケージで提供されるアラート機能を使用して、データベース
イベントの通知を行うことが可能です。トリガーの機能に似ていますが、トリガー
はDML文の実施ごとに発生するのに対し,アラートはトランザクション処理がコミッ
トされた時点で発行されます。(トランザクションがロールバックされるとアラートは
発生しません)
上の例では、アプリケーションAによるEMP表の更新を自動的にアプリケーショ
ンBに通知する例になります。
DBMS_PIPEとの違いは、
• DBMS_ALERTはシグナルを送るトランザクションがCOMMITされてか
らアラートを送る。
• アラート発生時にユーザーの要求が無ければ受け取らない。
• (DBMS_ALERTはWAITしつつアラートを待つのに対して、
DBMS_PIPEはメッセージをPIPEに取りにいく)
ということです。
参照:『アプリケーション開発者ガイド リリース8.0』
章
リリース 』 (A56822-1) - 16章
13
以下サンプルです。
実行方法は、以下の通りです。
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;
DBMS_ALERT.REGISTER(アラート名)
timeout number := 60;
begin
/* アラートに関心のあることを登録 */
dbms_alert.register(namex);
commit;
/* シグナルを待つ(
シグナルを待つ(タイムアウト60
タイムアウト60秒
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;
/
DBMS_ALERT.WAITANY(アラート名,メッセージ,ステータス,タイムアウト)
TESTSIGNAL.SQL
-------------declare
name varchar(50) := 'aaa';
msg varchar(50) := 'テスト・メッセージ
'テスト・メッセージ';
テスト・メッセージ';
begin
dbms_alert.signal(name,msg);
commit;
end;
/
DBMS_ALERT.SIGNAL(アラート名,メッセージ)
----------------- 以下、実行結果 ----------------------SQL> @TESTREGISTER.SQL
name=AAA
msg=テスト・メッセージ
msg=テスト・メッセージ
status=0,timeout=60
PL/SQLプロシージャが正常に完了しました。
PL/SQLプロシージャが正常に完了しました。
14
組み込みパッケージ
- DBMS_JOB
z
ジョブのスケジューリング
–
ジョブの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
proc1’,SYSDATE,
,SYSDATE,’SYSDATE+1
DBMS_JOB.SUBMIT(JOB#, proc1
,SYSDATE, SYSDATE+1’);
SYSDATE+1 );
END;
参照:『Oracle8
Server 管理者ガイド リリース8.0』
章
参照:『
リリース 』 (A56816-1) - 7章
15
組み込みパッケージ
- DBMS_UTILITY
z
便利なユーティリティ
P
P
P
P
ANALYZE_SCHEMA
COMPILE_SCHEMA
COMMA_TO_TABLE
TABLE_TO_COMMA
アナライズ(解析)
コンパイル
カンマ区切りの文字列をPL/SQL
カンマ区切りの文字列をPL/SQL表に格納
PL/SQL表に格納
PL/SQL表からカンマ区切りの文字列に移動
PL/SQL表からカンマ区切りの文字列に移動
F
F
F
F
FORMAT_ERROR_STACK
FORMAT_CALL_STACK
GET_TIME
PORT_STRING
現在のエラースタック入手(参照:
現在のエラースタック入手(参照:Page43,44)
Page43,44)
現在のコール・スタック入手(参照:
現在のコール・スタック入手(参照:Page42)
Page42)
経過時間(参照:
経過時間(参照:Page46)
Page46)
Oracle、
Oracle、OS情報
OS情報
P:プロシージャ、F:
:プロシージャ、F:ファンクション
F:ファンクション
ANALYZE_SCHEMA、
、COMPILE_SCHEMA
指定されたオブジェクトをアナライズ/コンパイルします。
スキーマ全体の一括処理
COMMA_TO_TABLE,TABLE_TO_COMMA
PLSQL表 ←→ カンマ区切り文字列
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_STRING
Oracleのバージョン(init.oraのCOMPATIBLEの内容)、OS情報
(例)
SunSolaris2.6上で、Oracle8.0.5の実行例: SVR4-be-8.0.0
WindowsNT4.0上のOracle8.1.5の実行例: IBMPC/WIN_NT-8.1.0
16
組み込みパッケージ
- その他
z
DBMS_LOCK.SLEEP
… 特定時間セッションを中断(秒数を指定)
z
DBMS_RANDOM
Oracle8i
… ランダム値を生成(-9999999999~
~9999999999)
)
ランダム値を生成(
z
DBMS_UTILITY. EXEC_DDL_STATEMENT
Oracle8i
… DDL文を実行
文を実行
DBMS_RANDOM,DBMS_UTILITY.EXEC_DDL_STATEMENTについては、
Oracle8ではマニュアルに記述が有りませんので、サポート対象外です。
Oracle8iでサポートします。
以下、3つのパッケージの利用例です。
DBMS_RANDOMを利用するには、
$ORACLE_HOME/rdbms/admin/catoctk.sqlを実行してください。
SET SERVEROUTPUT ON
BEGIN
DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP TABLE SOME_TABLE');
DBMS_UTILITY.EXEC_DDL_STATEMENT('CREATE TABLE TEST (A NUMBER)');
END;
/
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;
/
17
DDL文の実行をOracle8で行うにはには動的SQL(DBMS_SQL)を使います。
DBMS_RANDOMについては、以下の例のようにすると簡単な疑似乱数が取得
できます。
create or replace package random
is
procedure srand( new_seed in number );
function rand return number;
procedure get_rand( r OUT number );
function rand_max( n IN number ) return number;
procedure get_rand_max( r OUT number, n IN number );
end random;
/
実行
declare
i pls_integer;
randno pls_integer;
begin
random.srand(92541);
random.get_rand_max(i,92541);
dbms_output.put_line(i);
i:=1;
for i in 1..10 loop
random.get_rand(randno);
dbms_output.put_line(i||' | '||randno);
end loop;
end;
/
create or replace package body random
is
multiplier
constant number
:= 22695477;
increment
constant number
:= 1;
"2^32"
constant number
:= 2 ** 32;
"2^16"
constant number
:= 2 ** 16;
"0x7fff"
constant number
:= 32767;
Seed
number := 1;
-procedure srand( new_seed in number )
is
begin
Seed := new_seed;
end srand;
-function rand return number
is
begin
seed := mod( multiplier * seed + increment, "2^32" );
return bitand( seed/"2^16", "0x7fff" );
end rand;
-procedure get_rand( r OUT number )
is
begin
r := rand;
end get_rand;
-function rand_max( n IN number ) return number
is
begin
return mod( rand, n ) + 1;
end rand_max;
-procedure get_rand_max( r OUT number, n IN number )
is
begin
r := rand_max( n );
end get_rand_max;
-begin
srand( userenv( 'SESSIONID' ) );
end random;
/
grant execute on random to public
/
create public synonym random for random
/
18
コード設計/
コード設計/開発のヒント(1)
開発のヒント(1)
z
モジュール名・パラメータ名
–
z
独立したモジュール
–
z
パラメータ・モード
再利用
パラメータ設計のヒント
–
グローバル変数の利用に注意。
–
コメントを付ける。
PROCEDURE calc_elapsed_time
(start_time_in IN NUMBER
/* スタート時間 値域:0
値域:0~24 */
start_loc_in IN VARCHAR2 /* 出発地:NY,CHI,SF
出発地:NY,CHI,SF */
end_loc_in
IN VARCHAR2 /* 目的地:DC,LA,NO
目的地:DC,LA,NO */;
プロシージャ、関数、パラメータの使用についての効果的なPL/SQLプログラミン
グの方法をまとめます。
モジュール名・パラメータ名
• モジュールの動作内容の分かるモジュール名をつける。
• 仮パラメータのパラメータ・モードを使いパラメータの種類が分かるよう
にする。ただし、ファンクションにはOUTパラメータは使わない。
独立したモジュール
• モジュールを作成するときには、現在のシステムとそれ以外のシステム
でも再利用できるような書き方をする。
• コードの記述量が減り、バグの発生も開発時間も減少する。
パラメータ設計のヒント
• グローバル変数はできるだけ使わず、パラメータを使用する。
(モジュールのインターフェイスのみで使用方法が分かるように)
• パラメータと機能についてコメントをつける。
19
コード設計/
コード設計/開発のヒント(2)
開発のヒント(2)
ファンクション(関数)の利用
z
–
–
–
汎用性のある関数を作る。
関数は1つ値を返すようにコーディングする。
つ値を返すようにコーディングする。OUT
関数は
つ値を返すようにコーディングする。
パラメータは使用しないようにする。
例外処理
–
無効なパラメータのトラップ
ファンクション(関数)の利用
ファンクションは、式の一部としてコールされます。
IF sal_ok(new_sal, new_title) THEN ...
使用しやすさから、ファンクションは複数の値を返すようにコーディングす
べきでは有りません。
----- 以下、関数の例 --------------------------------------------FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN VARHAR2
IS
return_value VARCHAR2(20) := NULL;
BEGIN
/* 有効なパラメータの検査 */
C’ THEN return_value := ‘CLOSED
CLOSED’;
IF status_cd_in = ‘C
CLOSED ;
ELS IF status_cd_in = ‘O
O’ THEN return_value := ‘OPEN
OPEN’;
OPEN ;
ELSE
RAISE value_error;
END IF;
RETURN return_value;
EXCEPTION
WHEN value_error THEN
RETURN NULL;
END;
20
コード設計/
コード設計/開発のヒント(3)
開発のヒント(3)
ローカル・モジュールの活用
z
–
コードの重複をなくす
ローカル・モジュール :
宣言部内に定義されたプロシージャ / ファンクション
有効範囲は宣言されているモジュール内
ローカル・モジュールの活用
以下の例では、実行部で繰り返し利用される処理をファンクション(NPV)
として、宣言部に定義しています。これによって処理の定義を繰り返し行
わなくてすみます。
PROCEDURE format_data
(projected_sales_in IN NUMBER,year_in IN INTEGER)
IS
total_cost
NUMBER(9);
gross_profit NUMBER(9);
crew_labor
mgmt_labor
宣言部
NUMBER(9);
NUMBER(9);
/* ---------- ローカル・モジュール ----------*/
FUNCTION npv (column_in IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN
TO_CHAR((net_present_value ( ‘‘ || column_in, year_in) /
*100),’999.99
999.99’);
projected_sales_in *100),
999.99 );
END;
BEGIN
:owner.total_cost_pc := npv(‘total_cost
npv( total_cost’);
total_cost );
:owner.gross_profit_pc
:owner.gross_profit_pc := npv(‘grew_profit
npv( grew_profit’);
grew_profit );
実行部
:owner.crew_labor_pc
:owner.crew_labor_pc := npv(‘crew_labor
npv( crew_labor’);
crew_labor );
:owner.mgmt_labor_pc
:owner.mgmt_labor_pc := npv(‘mgmt_labor
npv( mgmt_labor’);
mgmt_labor );
END;
21
コード設計/
コード設計/開発のヒント(4)
開発のヒント(4)
z
LOOP、無名ブロックにラベルを付ける
、無名ブロックにラベルを付ける
<<ラベル
<<ラベル>>
ラベル>>
z
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;
/
22
コード設計/
コード設計/開発のヒント(5)
開発のヒント(5)
z
パッケージ化
プロシージャ
• 利点
ファンクション
変数宣言
定数宣言
カーソル宣言
例外宣言
-
モジュール性
トップダウンアプリケーション設計
情報隠蔽
永続的状態
パフォーマンスの向上
モジュール性
理論的に関連するオブジェクトをまとめる
トップダウンアプリケーション設計
パッケージ内の手続き部分のコーディング以前に、呼び出し方法のみを
登録できる。
情報隠蔽
外部から使用できる要素(パブリック・プロシージャ、グローバル変数)と、
パッケージ内でのみ使用できる要素(プライベート・プロシージャ、ローカ
ル変数)を区別できる。
永続的状態
変数やカーソルはセッションを通じてその内容を保持する。
単一Oracleセッション内でグローバル
パフォーマンスの向上
パッケージ内の要素が呼び出されると、パッケージ全体がメモリにロード
される。
23
パッケージ仕様部
• 仕様部分のみの記述
• 仕様部分のみでコンパイル可能
CREATE PACKAGE package_name
IS
[ 変数と型の宣言 ]
[ カーソルの仕様 ]
[モジュールの仕様]
モジュールの仕様]
PROCEDURE ...(..);
FUNCTION ...(..);
END; [ パッケージ名 ]
パッケージ本体
• カーソルとモジュールの完全な実装
CREATE PACKAGE BODY package_name
IS
[ 変数と型の宣言 ]
[ カーソルの仕様とSELECT
カーソルの仕様とSELECT文
SELECT文 ]
[モジュールの仕様と本体]
モジュールの仕様と本体]
PROCEDURE ...(..)
IS
BEGIN
処理
END;
FUNCTION ...(..)
IS
BEGIN
処理
END;
[ BEGIN
実行文 ]
[ EXCEPTION
例外ハンドラ ]
END; [ パッケージ名 ]
24
コード設計/
コード設計/開発のヒント(6)
開発のヒント(6)
変数の宣言
z
–
%TYPE
my_empno emp.
emp.empno%TYPE;
empno%TYPE;
–
%ROWTYPE
emp_rec emp%ROWTYPE;
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で取り出された行を格納できます。
25
その他 TIPS
- コレクション
z
PL/SQL表
表 Vs. Nested Table & Varray
PL/SQL表
PL/SQL表
PL/SQL内
PL/SQL内
z
Nested Table &Varray
&Varray
PL/SQL内、データベース内
PL/SQL内、データベース内
Nested Table と Varray
要素数の制限の指定
順序付け
Varray
Nested Table
必要あり
あり
必要なし
なし
PL/SQL リリース2(Oracle7)までは、PL/SQL表という一次元の配列をサポートし
ていました。
PL/SQL リリース8.0(Oracle8)からはNested TableとVarrayをサポートします。
VARRAYは要素の数の制限を指定する必要があります。
Nested Tableは上限がないため各オブジェクトの変更が自由に行えます。
VARRAYと
とNested Tableは、
は、PL/SQL内で利用する場合には他に違いは有り
内で利用する場合には他に違いは有り
は、
ません。
*現時点PL/SQL
リリース8.0(
*現時点
リリース (Oracle8 リリース8.0)では、
リリース )では、3つのデータ型ともサ
)では、 つのデータ型ともサ
ポートしていますが、新規に作成される場合PLSQL表より
表よりNested
Tableを使うこ
を使うこ
ポートしていますが、新規に作成される場合
表より
とをお勧めします。
26
declare
type color_tab_type is table of varchar2(10)
index by binary_integer;
color_tab color_tab_type;
begin
color_tab(1) := 'Black';
color_tab(2) := 'White';
color_tab(3) := 'Green';
color_tab(4) := 'Red';
PL/SQL表
PL/SQL表
for i in 1..color_tab.count
loop
dbms_output.put_line(color_tab(i));
end loop;
end;
/
declare
type Color_tab_t is table of varchar2(10);
colors Color_tab_t := Color_tab_t();
begin
colors.extend(4);
colors(1) := 'Blue';
colors(2) := 'Yellow';
colors(3) := 'Gold';
colors(4) := 'Silver';
Nested Table
for i in 1..colors.count
loop
dbms_output.put_line(colors(i));
end loop;
end;
/
declare
type Color_varray_t is varray(16) of varchar2(10);
colors Color_varray_t := Color_varray_t();
begin
colors.extend(4);
colors(1) := 'Pink';
colors(2) := 'Purple';
colors(3) := 'Gray';
colors(4) := 'Orange';
Varray
for i in 1..colors.count
loop
dbms_output.put_line(colors(i));
end loop;
end;
/
27
その他 TIPS
- ラージ・オブジェクト(LOB
ラージ・オブジェクト(LOB)
LOB)
z
z
… データベース表領域内に格納
内部LOB
内部
–
BLOB - バイナリファイル
–
バイト固定幅の文字データ
CLOB - 1バイト固定幅の文字データ
–
NCLOB -マルチバイト固定幅の文字データ
… 表領域外に格納、OS
外部LOB
ファイル
外部
表領域外に格納、
–
BFILE - バイナリ・ファイル
DBMS_LOBパッケージ
パッケージ … PL/SQLで
でLOBを操作
を操作
Oracle8ではLOB(4GBまでのRAWまたはバイナリ・データ、文字データ)を格納できるデータ型
、SQL DDL/DMLコマンド、OCI/PLSQLのAPIを提供しています。
• LOBはレプリケーションすることも可能(BFILEを除く)
• 一つの表に複数のカラムを持つことも可能です。
• 格納方法もOracleの内部はもちろん、外部にも格納することができます。
• データベース・バッファを有効に利用するために、LOB型データをバッファリングする
かどうかの指定をすることが可能です。
• ロギングも行うかどうかの指定ができます。
DBMS_LOB
APPEND
LOB値を別の
LOB値を別のLOB
値を別のLOBに追加します。
LOBに追加します。
COPY
LOBの一部を別の
LOBの一部を別のLOB
の一部を別のLOBにコピーします。
LOBにコピーします。
ERASE
指定のオフセットから開始して、LOB
指定のオフセットから開始して、LOBの一部を消去します。
LOBの一部を消去します。
LOADFROMFILE
BFILEデータを内部
BFILEデータを内部LOB
データを内部LOBにロードします。
LOBにロードします。
WRITE
指定されたオフセットからLOB
指定されたオフセットからLOBにデータを書き込みます。
LOBにデータを書き込みます。
GETLENGTH
LOB値の長さを取得します。
LOB値の長さを取得します。
READ
指定されたオフセットからLOB
指定されたオフセットからLOBデータを読み込みます。
LOBデータを読み込みます。
FILEOPEN
ファイルをオープン。
FILECLOSE
ファイルをクローズ。
OPEN
LOBを
LOBをOPEN。
OPEN。
CLOSE
LOBを
LOBをCLOSE。
CLOSE。
28
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カラムを初期化、空のBLOB
カラムを初期化、空のBLOB値と共に
BLOB値と共に1
値と共に1行INSERT、ロケータを返す
INSERT、ロケータを返す */
INSERT INTO TEMP VALUES (1, EMPTY_BLOB(), FILS)
RETURNING IMG_IN_DB INTO LOBD;
/* LOB値に実データを挿入
LOB値に実データを挿入 */
DBMS_LOB.LOADFROMFILE(LOBD, FILS, AMT);
COMMIT;
/* ファイルをクローズ */
DBMS_LOB.FILECLOSE(FILS);
/* BFILE , BLOBに格納された内容を読込、出力
BLOBに格納された内容を読込、出力 */
SELECT IMG_IN_DB, IMG_OUT_DB INTO LOBD, FILS FROM TEMP;
AMT:=DBMS_LOB.GETLENGTH(FILS);
DBMS_OUTPUT.PUT_LINE(‘BFILE:
||AMT);
DBMS_OUTPUT.PUT_LINE( BFILE: ’||AMT);
AMT:=DBMS_LOB.GETLENGTH(LOBD);
DBMS_OUTPUT.PUT_LINE(‘BLOB:
||AMT);
DBMS_OUTPUT.PUT_LINE( BLOB: ’||AMT);
END;
/
実行結果:
BFILE: 9
BLOB: 9
29
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
DECLARE
LOB_LOC
CLOB;
BUFFER
VARCHAR2(2); /* 読込バッファ*/
読込バッファ*/
AMOUNT
BINARY_INTEGER := 2;
LENGTH
BINARY_INTEGER;
POSITION
INTEGER := 1;
BEGIN
SELECT B INTO LOB_LOC FROM TEST;
DBMS_LOB.OPEN(LOB_LOC, DBMS_LOB.LOB_READONLY);
/* CLOBの内容の長さを入手
CLOBの内容の長さを入手 */
LENGTH := DBMS_LOB.GETLENGTH(LOB_LOC);
/* オフセットを2
オフセットを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
NCLOB等を使用するためには、各国語キャラクタ・セットに固定幅マルチバイト・キャラクタ
NCLOB等を使用するためには、各国語キャラクタ・セットに固定幅マルチバイト・キャラクタ
・セットを指定してください。
CREATE DATABASE時に
DATABASE時にNATIONAL
時にNATIONAL CHARACTER SETに適切なキャラクタセット
SETに適切なキャラクタセット(
に適切なキャラクタセット(JA16SJISFIXEDや
JA16SJISFIXEDや
JA16EUCFIXED等)を指定します。
JA16EUCFIXED等)を指定します。
また、一度設定した値をCREATE
また、一度設定した値をCREATE DATABASE後に変更することはできません。
DATABASE後に変更することはできません。
30
コードの管理
z
ラッピング
z
データベース内のコード
–
USER_OBJECTS
–
USER_OBJECT_SIZE
–
USER_SOURCE
–
USER_DEPENDENCIES
31
コードの管理
- ラッピング
z
PL/SQL ラッパー
–
PL/SQLソースコードをオブジェクトコードの
ソースコードをオブジェクトコードの
中間形式に変換する。
–
アプリケーションの内部を隠す。
以下のコマンドを実行
WRAP INAME=input_file
INAME=input_file [ONAME=output_file
[ONAME=output_file]
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)
IS
BEGIN
UTL_FILE.GET_LINE (file_in, line_out);
eof_out := FALSE;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
line_out := NULL;
eof_out := TRUE;
END;
ラップ後
CREATE OR REPLACE PROCEDURE get_nextline wrapped
0
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
。。
32
コードの管理
- DB内のコード
DB内のコード(1)
内のコード(1)
z
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 66
COLUMN object_type FORMAT A20
COLUMN object_name FORMAT A30
COLUMN status FORMAT A10
BREAK ON object_type SKIP 1
SPOOL psobj.lis
SELECT object_type, object_name, status
FROM user_objects
WHERE object_type IN ('PACKAGE', 'PACKAGE BODY', 'FUNCTION',
'PROCEDURE')
ORDER BY object_type, status, object_name
/
SPOOL OFF
33
コードの管理
- DB内のコード
DB内のコード(2)
内のコード(2)
z
USER_OBJECT_SIZE
–
PL/SQLコードサイズの分析
コードサイズの分析
火 May 25
ページ
1
Size of PL/SQL Objects > 2000 Bytes
NAME
CODE_SIZE
TYPE
SOURCE_SIZE PARSED_SIZE
------------------ -------------- ----------- ----------- -------DBMS_REPCAT_AUTH
3130
PACKAGE BODY
10712
0
USER_OBJECT_SIZE:ユーザーが所有するオブジェクトのサイズ
:ユーザーが所有するオブジェクトのサイズ
ストアド・オブジェクトのコンパイル済みのコードは、DBのSGAにロードさ
れ実行されるので、あまりに大きなプログラムは避けるべきです。(大きな
プログラムをSGAに確保するとそれ以外のプログラムがSGAから排除さ
れてしまい、次回の実行時にロードし直さなくてはならない為)
以下のスクリプトをSQL*Plusで実行すると、上のように比較的大きなモ
ジュールを検索できます。
SET PAGESIZE 66
COLUMN name FORMAT A30
COLUMN type FORMAT A15
COLUMN source_size FORMAT 999999
COLUMN parsed_size FORMAT 999999
COLUMN code_size FORMAT 999999
TTITLE 'Size of PL/SQL Objects > 2000 Bytes'
SPOOL pssize.lis
SELECT name, type, source_size, parsed_size, code_size
FROM user_object_size
WHERE code_size > 2000
ORDER BY code_size DESC
/
SPOOL OFF
34
コードの管理
- DB内のコード
DB内のコード(3)
内のコード(3)
z
USER_SOURCE
–
ソース・コードの相互参照
–
行番号でソースを検索
LINE TEXT
---------1
2
3
4
5
6
-------------------------------------------------PROCEDURE get_nextline
(file_in
(file_in IN UTL_FILE.FILE_TYPE,
line_out OUT VARCHAR2,
eof_out OUT BOOLEAN)
IS
BEGIN
USER_SOURCE:作成したオブジェクトのソース
:作成したオブジェクトのソース
以下のスクリプトをSQL*Plusで実行すると上の例のように、ソースが参照
できます。
これによって、PL/SQLが表示するエラーの発生行を特定することができ
ます。
column text format a50
select line,text from user_source
where name=UPPER(‘&1’) /* オブジェクトの名前 */
and type = 'PROCEDURE' /* オブジェクトのタイプ */
order by line;
35
コードの管理
- DB内のコード
DB内のコード(4)
内のコード(4)
z
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 a12
column To_ format a12
column To_TYPE format a12
column To_OWNER format a12
column To_LINK format a12
SELECT name From_,
referenced_name To_,
referenced_type To_type,
referenced_owner To_owner,
referenced_link_name To_link
FROM user_dependencies
WHERE referenced_owner = UPPER ('scott')
AND referenced_type = UPPER ('procedure');
36
PL/SQLのデバッグ
PL/SQLのデバッグ
z
コンパイル時のエラー表示
z
実行時の変数、式の値
z
Tool
z
コール・スタック情報
z
エラー・スタック情報
37
PL/SQLのデバッグ
PL/SQLのデバッグ
- コンパイル時のエラー表示
z
SHOW ERRORS (SQL*Plus)
)
–
コンパイル時のエラーを表示する
警告:
警告: プロシージャが作成されましたが、
コンパイル・エラーがあります。
プロシージャ、パッケージのコンパイル時のエラーはSQL*Plusを用いている場
合、SHOW ERRORSコマンドを使用して表示することができます。
ここでのエラーの行番号は、USER_SOURCE(参照:Page35)で確認してください。
SQL> SHOW ERRORS PROCEDURE ANCHOR_TST
PROCEDURE ANCHOR_TSTのエラーです。
ANCHOR_TSTのエラーです。
LINE/COL ERROR
-------- ----------------------------------------------------------------3/10
PLS-00201: 識別子:
識別子: PLS_ANCHOR_TEST.AAAを宣言してください。
PLS_ANCHOR_TEST.AAAを宣言してください。
3/10
PL/SQL: Item ignored
4/16
PL/SQL: SQL Statement ignored
4/32
PLS-00201: 識別子:
識別子: PLS_ANCHOR_TESTを宣言してください。
PLS_ANCHOR_TESTを宣言してください。
7/3
PL/SQL: SQL Statement ignored
7/17
PLS-00320: この式の型の宣言が不完全か、または形式が誤っています。
8/3
PL/SQL: Statement ignored
8/24
PLS-00320: この式の型の宣言が不完全か、または形式が誤っています。
38
PL/SQLのデバッグ
PL/SQLのデバッグ
- 実行時の変数、式の値
z
変数および式の値
–
標準出力
DBMS_OUTPUTパッケージ
–
ダミー表に挿入
Insert + Commit
–
PIPE
DBMS_PIPEパッケージ
–
外部ファイル(OSファイル)に出力
外部ファイル( ファイル)に出力
UTL_FILEパッケージ
DBMS_OUTPUTのPUT文、PUT_LINE文を使用して、変数および式の値を端
末に出力できます。
パッケージなどを作成して、使いやすいように改良。 簡単な入力ですむようにな
ど
以下、利用方法と内容が確認できるタイミングの違いについてまとめました。
DBMS_OUTPUT (標準出力) (参照: Page6)
)
実行後にまとめて出力されます。以下の例では、約10秒後に全結果が
出力されます。
set serveroutput on
declare
temp_no pls_integer :=1;
begin
for i in 1..10 loop
dbms_output.put_line('Temp No is ' ||temp_no);
dbms_lock.sleep(1);
temp_no := temp_no+1;
end loop;
end;
/
39
ダミー表
変数の値を格納する為の一時表を作成しておき、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;
…
PIPE
DBMS_PIPEパッケージを使用しPIPE内にメッセージとして、変数の値を
送信します。他セッションで値の確認を行います。(参照:Page11,12)
要求を出した時点での、参照が可能です。
…
for i in 1..10 loop
dbms_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パッケージを使用します。(参照:Page7,8)
実行ログとしても利用が可能です。
UTL_FILE.FFLUSHを行えば逐次の参照が可能です。以下の例では、
毎行の情報を外部ファイルから取得することができます。(例えばUNIX
コマンド“tail”など)
…
fhandle := utl_file.fopen('/tmp','debug.log','w');
for i in 1..10 loop
utl_file.put_line(fhandle,'Temp No is ' ||temp_no);
utl_file.fflush(fhandle);
dbms_lock.sleep(1);
temp_no := temp_no+1;
end loop;
…
40
PL/SQLのデバッグ
PL/SQLのデバッグ
- Tool
z
Oracle Procedure Builder
–
Oracle Developer/2000の一部
の一部
Oracle Procedure Builderを使用すると、ブレークポイントの設定、変数の値のリ
スト、その他のデバッグ作業が行えます。
41
PL/SQLのデバッグ
PL/SQLのデバッグ
- コール・スタック情報
z
コール・スタックの取得
DBMS_UTILITY.FORMAT_CALL_STACK
Procedure
proc1
呼び出し
Procedure
proc2
呼び出し
Procedure
proc3
実行
DBMS_UTILITYのファンクションを使用して、現在のコール・スタック及びエ
ラー・スタックを取得できます。
例えば、
dbms_output.put_line(dbms_utility.format_call_stack);
を、エラーの発生する直前に、挿入しておけば、エラー発生時のコール・スタック
情報が取得できます。
以下の例では、上の図のような関係のプロシージャのうち、プロシージャproc1の
中でコールスタックを取得した結果です。
呼び出したモジュールと、その行数が分かります。
----- PL/SQL Call Stack ----object
line
handle
number
object
name
809612c8
4
procedure YHAYASHI.PROC1
80960268
4
procedure YHAYASHI.PROC2
8095c77c
8095c77c
4
procedure YHAYASHI.PROC3
808b6318
2
anonymous block
42
PL/SQLのデバッグ
PL/SQLのデバッグ
- エラー・スタック情報
z
エラー・スタックの取得
DBMS_UTILITY.FORMAT_ERROR_STACK
実行
プロシージャ:A
プロシージャ:A
例外発生
ttt_insert
実行
プロシージャ:B
プロシージャ:B
INSER
T
表:TTT
表:TTT
トリガー
(失敗)
また、
dbms_output.put_line(dbms_utility.format_error_stack);
を例外処理の中で使用すれば発生した一連のエラー・スタックが入手できます。
次のページの例では、トリガー内でのエラー情報がプロシージャ(B)内の例外処
理で取得できています。プロシージャ(A)の処理は例外処理を行わないのでエ
ラー情報は取得していません。
43
CREATE TABLE ttt (f1 number);
CREATE OR REPLACE TRIGGER ttt_insert
BEFORE INSERT ON ttt
BEGIN
例外を発生させる
RAISE ZERO_DIVIDE;
END ttt_insert;
/
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", 行:
2
ORA-04088: トリガー:
トリガー: YHAYASHI.TTT_INSERTの実行中にエラーが発生しました。
YHAYASHI.TTT_INSERTの実行中にエラーが発生しました。
##
44
PL/SQLのチューニング
PL/SQLのチューニング
z
パフォーマンス分析
z
アクセス
z
アルゴリズム
45
PL/SQLのチューニング
PL/SQLのチューニング
- パフォーマンス分析
z
コード内で、パフォーマンスに悪影響を与えて
いる個所を特定します。
DBMS_UTILITY.GET_TIME
: 時間(1/100
時間(1/100秒
(1/100秒)を返します。
以下、ある処理(ここでは3秒間SLEEPさせています)の実行時間を表示するサン
プルです。
DECLARE
time_before BINARY_INTEGER;
time_after BINARY_INTEGER;
BEGIN
time_before := DBMS_UTILITY.GET_TIME;
--- 任意の処理が入ります。 calc_totals;
--- ここでは、3秒間の
を行わせています。
ここでは、 秒間のSLEEPを行わせています。
秒間の
DBMS_LOCK.SLEEP(3);
time_after := DBMS_UTILITY.GET_TIME;
DBMS_OUTPUT.PUT_LINE(time_after - time_before);
END;
/
<<実行結果>>
301
PL/SQLプロシージャが正常に完了しました。
プロシージャが正常に完了しました。
46
PL/SQLのチューニング
PL/SQLのチューニング
- アクセス
z
重要なコードはSGAに固定
に固定
重要なコードは
–
DBMS_SHARED_POOL.KEEP : 固定する
–
DBMS_SHARED_POOL.UNKEEP : 解放する
z
自動再コンパイルを避ける
z
クライアント側SQLを避ける
を避ける
クライアント側
重要なコードはSGAに固定する
重要なコードは
に固定する
ストアド・モジュールを実行する際には、コンパイル済みのコードがSGA
にロードされている必要が有ります。すでにSGAにロードされていればそ
の分、実行時間は速くなります。もっとも使用するパッケージは常にメモリ
上に有るようにすると効果的です。
KEEP実行時にはロードされず、パッケージ実行時に実際はロードされま
す。DB起動直後のSGAが断片化していないときにパッケージの固定を
行うことが推奨されています。
他のリソースの使用量も考えて固定するパッケージについて検討する必
要が有ります。
例えば、以下のようなパッケージの固定が考えられます。
STANDARD、
、DBMS_STANDARD、
、DBMS_UTILITY、
、
DBMS_DESCRIBE、
、DBMS_OUTPUT、
、DBMS_LOCK、
、
DBMS_PIPE
自動再コンパイルを避ける。
参照:Page 33
クライアント側SQLを避ける
を避ける
クライアント側
同じSQLコードが複数のプログラムで共有しやすくなり、解析済みの共有
SQLコードの利用率が高まります。
プロシージャや関数にまとめることで、ネットワークに1回アクセスするだ
けで一連の操作が実行できます。
47
PL/SQLのチューニング
PL/SQLのチューニング
- アルゴリズム(1)
アルゴリズム(1)
z
ローカル変数の使用
z
PLS_INTEGER
z
NOT NULL制約を避ける
制約を避ける
ローカル変数の使用
パラメータ値をローカルコピーを作成しコピーする。
これにより、以下の左の例では繰り返しUPPER関数を呼び出しているの
に対して、右の例では1回の呼び出しですみます。
PROCEDURE calc_sales
(company_id IN NUMBER, action_in IN VARCHAR2)
IS
PROCEDURE calc_sales
(company_id IN NUMBER, action_in IN VARCHAR2)
IS
BEGIN
IF UPPER (action_in) = ‘ANNUAL’ THEN ...
action_int VARCHAR2(10) := UPPER(action_in);
BEGIN
ELSIF UPPER (action_in) = ‘QUARTERLY’ THEN ...
IF action_int = ‘ANNUAL’ THEN ...
ELSIF UPPER (action_in) = ...
ELSIF action_int = ‘QUARTERLY’ THEN ...
END IF;
ELSIF action_int = ...
END;
END IF;
END;
48
PL/SQLのチューニング
PL/SQLのチューニング
- アルゴリズム(2)
アルゴリズム(2)
z
ローカル変数の使用
z
PLS_INTEGERデータ型
データ型
z
NOT NULL制約を避ける
制約を避ける
PLS_INTEGERデータ型
データ型
符号付き整数(-2147483647 ~ 2147483647)の操作には
PLS_INTEGERデータ型の使用が一番効率が良い。(記憶領域、処理
速度の点で)
NOT NULL制約を避ける
制約を避ける
PL/SQL内の変数にNOT NULL制約をつけると、変数の代入毎に一時
変数を使い値の検証を行ってしまいます。避ける為にはプログラミングで
制約を実現します。
PROCEDURE foo IS
m number NOT NULL;
BEGIN
m := a+b;
m := m * 1.2;
m := m * m;
...
END;
PROCEDURE foo IS
m number;
BEGIN
m := a+b;
m := m * 1.2;
m := m * m;
if (m is null) then
----- エラー処理 ----end if;
...
END;
49
参考
- technet.oracle.com
technet.oracle.com
http://technet
http://technet.oracle.com
technet.oracle.com
技術情報
開発ツール
の紹介
Sample
Code and
Tips
Free
Utiliities
•技術情報
技術情報
• 開発ツールの紹介
商用開発ツール、Emacs(Free)
• Sample Code and Tips
依存関係トレース
• Free Utiliities
Random Generator
Bitwise Operation
50
この文書はあくまでも参考資料であり、掲載されている情報は予告なしに変更さ
れることがあります。この文書に関連して不都合が生じた場合も、米国オラクル
社及び日本オラクル株式会社は一切保証せず、特に責任は負いかねますので
ご容赦下さい。また許可なく、改編、引用することを禁じます。
51
Fly UP