Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
<Insert Picture Here>
Oracle Direct Seminar
実践!!PL/SQLチューニング日本オラクル株式会社
Copyright© 2009, Oracle. All rights reserved. 2
<Insert Picture Here>
アジェンダ
• はじめに• パフォーマンス向上の為のPL/SQLの機能紹介• パフォーマンスを意識したコーディング
※資料中に掲載されている検証内容は、特定の環境に
おける検証結果についての報告であり、すべての環境において同様の動作を保証するものではありませんので予めご了承下さい。
・SQL Serverからの移行アセスメント・MySQLからの移行相談・PostgreSQLからの移行相談・Accessからの移行アセスメント・Application Server 移行相談・Oracle Database バージョンアップ支援・Oracle Developer/2000 Webアップグレード相談・パフォーマンス・クリニック・Oracle Database 構成相談・Oracle Database 高可用性診断・システム連携アセスメント
Oracle Directの無償技術サービス
http://www.oracle.com/lang/jp/direct/services.html
Copyright© 2009, Oracle. All rights reserved. 3
はじめに
• 前提• PL/SQLの基本的な知識がある方を対象としています• 原則として動作確認はOracle Database 11g Enterprise Edition
11.1.0.6にて実施しています※記述がなくても古いバージョンでは使えない可能性があります
• 動作検証環境• マシン:仮想マシン環境• OS: Oracle Enterprise Linux R5 update1 (32bit)• インスタンスはDBCAで全てデフォルトで作成、追加チューニング一切なし
Copyright© 2009, Oracle. All rights reserved. 4
チューニングにかかるコストと利益
アプリケーションの設計から本番稼動までの間のチューニングにかかるコストと利益
コスト
設計 開発 本番
時 間
チューニングにチューニングによって得られる利益よって得られる利益
本日の話
Copyright© 2009, Oracle. All rights reserved. 5
• 最適なコードを書く = 「無駄」を省く
PL/SQLのチューニングとは?
• アプリケーションとしての「無駄」を省く• 言語動作の内部的な動作の「無駄」を省く
•暗黙的に実行される処理を避ける•効率の悪い処理を避ける
•複数の処理を1回の処理で•繰り返し処理を1回の処理で
• 「まとめて」実行する機能を利用する
• SQLも含めて検討する
Copyright© 2009, Oracle. All rights reserved. 6
<Insert Picture Here>
アジェンダ
• はじめに• パフォーマンス向上の為のPL/SQLの機能紹介
• 言語動作の内部的な動作の「無駄」を省く① PL/SQL数値データ型の選択②変数宣言とNOT NULL制約③引数のNOCOPY指定④ PL/SQLファンクション結果キャッシュ⑤ PL/SQLネイティブ・コンパイル
• 「まとめて」実行する機能を利用する• SQLも含めて検討する(SQL新機能紹介)
• パフォーマンスを意識したコーディング
Copyright© 2009, Oracle. All rights reserved. 7
① PL/SQL数値データ型の選択
• 整数を扱う際は、BINARY_INTEGER, PLS_INTEGER, SIMPLE_INTEGERの利用がお勧め• SIMPLE_INTEGERはNULLチェック、オーバーフローチェックが不要な時に利用(チェックのオーバーヘッドが発生しません)
• SIMPLE_INTEGERはネイティブ・コンパイル(後述)時にパフォーマンス向上
整数
-231 ~ 231
○
×
SIMPLE_INTEGER
浮動小数浮動小数整数整数固定小数浮動小数
数値の種類
倍精度64bit単精度32bit-231 ~ 231-231 ~ 231
1E-130 ~9.99…9E 125、-1E-130 ~
- 9.99…9E 125、0
取り得る値の範囲
○○○○○PL/SQLデータ型
○○××○テーブルデータ型
BINARY_DOUBLE
BINARY_FLOAT
PLS_INTEGER
BINARY_INTEGERNUMBER
11g~
Copyright© 2009, Oracle. All rights reserved. 8
数値データ型によるパフォーマンスの差
CREATE OR REPLACE PROCEDURE num_prefxx ISwk_cnt データ型 := 0;
BEGINFOR cnt IN 0..1000000000 LOOP
wk_cnt := wk_cnt + 1;END LOOP;
END;/
CREATE OR REPLACE PROCEDURE num_prefxx ISwk_cnt データ型 := 0;
BEGINFOR cnt IN 0..1000000000 LOOP
wk_cnt := wk_cnt + 1;END LOOP;
END;/
numberbinary_integerpls_integersimple_integer
のいずれかを指定
※ネイティブ・コンパイラについては後述
0.04
0.12
0.14
0.82
0.34
0.34
0.33
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
simple_integer(NATIVE)
pls_integer(NATIVE)
binary_integer(NATIVE)
number(NATIVE)
simple_integer
pls_integer
binary_integer
number
Copyright© 2009, Oracle. All rights reserved. 9
②変数宣言とNOT NULL制約
• 変数宣言の際にNOT NULL制約をおこなうと、代入の度にNULLチェックがおこなわれるDECLAREvalue1 NUMBER NOT NULL := 0;
BEGINFOR cnt IN 0 .. 1000000000 LOOPvalue1 := 1;END LOOP;
END;/
DECLAREvalue1 NUMBER NOT NULL := 0;
BEGINFOR cnt IN 0 .. 1000000000 LOOPvalue1 := 1;END LOOP;
END;/
代入の度NULLチェックが行われる
0.44
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
NOT NULLなし
NOT NULLあり
DECLAREvalue1 NUMBER := 0;
BEGINFOR cnt IN 0 .. 1000000000 LOOPvalue1 := 1;
END LOOP;END;/
DECLAREvalue1 NUMBER := 0;
BEGINFOR cnt IN 0 .. 1000000000 LOOPvalue1 := 1;
END LOOP;END;/
頻繁に代入される場合、NOT NULL制約をはずす
Copyright© 2009, Oracle. All rights reserved. 10
③引数のNOCOPY指定
• PL/SQLサブプログラムの引数と引数モード
• 引数モード:
• NOCOPYヒントを付けるとOUT、IN OUTでも「参照渡し」が可能
• 「値渡し」は一時変数へのコピーが発生• サイズの大きな変数やデータ構造(レコード等)でメモリー消費や実行速度低下
:値渡し (call by value)IN OUT
: 値渡し (call by value)OUT
: 参照渡し (call by reference )IN
サブプログラム名 (パラメータ名 引数モード(NOCOPY) データ型, …);サブプログラム名 (パラメータ名 引数モード(NOCOPY) データ型, …);
Copyright© 2009, Oracle. All rights reserved. 11
NOCOPY指定によるパフォーマンスの差
DECLARETYPE employees_array_tblIS TABLE OF employees%ROWTYPEINDEX BY pls_integer;employees_array employees_array_tbl;cnt pls_integer := 0;PROCEDURE test(emp_array_out OUT NOCOPY employees_array_tbl)ISBEGINSELECT * BULK COLLECT INTO emp_array_outFROM employees;
END;BEGINtest(employees_array);
END;/
DECLARETYPE employees_array_tblIS TABLE OF employees%ROWTYPEINDEX BY pls_integer;employees_array employees_array_tbl;cnt pls_integer := 0;PROCEDURE test(emp_array_out OUT NOCOPY employees_array_tbl)ISBEGINSELECT * BULK COLLECT INTO emp_array_outFROM employees;
END;BEGINtest(employees_array);
END;/
指定なし・ありで比較
※ employees表:1,000,000件(全件)を 一括で結合配列に
0.73
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
NOCOPYあり
NOCOPYなし
Copyright© 2009, Oracle. All rights reserved. 12
④ PL/SQLファンクションの結果キャッシュ (その①)
• PL/SQLファンクションの結果をSGAにキャッシュし、複数のセッションで利用できるようになりました• PL/SQLファンクションに、結果をキャッシュすることを明記する必要あり• ファンクションおよびパラメータの値を組にして結果をキャッシュ• システムで必要なメモリが足りなくなると古いものから破棄される• ファンクション内で参照している表が変更されるとキャッシュは破棄
• 利用に適したファンクション• ほとんど変更されない情報に依存するもので頻繁に実行されるもの
• マスターテーブルから情報を取得するようなもの• 再帰処理をおこなうもので、何度も同じファンクション+パラメータを利用するもの
• 制限については以下のマニュアルを参考を参照してください• Oracle Database PL/SQL言語リファレンス 11g リリース1(11.1)
11g
EE
Copyright© 2009, Oracle. All rights reserved. 13
PL/SQLファンクションの結果キャッシュ (その②)CREATE OR REPLACE PACKAGE mst IS
FUNCTION get_price(item_id_in IN NUMBER, date_in IN DATE)
RETURN NUMBER RESULT_CACHE;
END mst;
/
CREATE OR REPLACE PACKAGE BODY mst IS
FUNCTION get_price(item_id_in IN NUMBER, date_in IN DATE)
RETURN NUMBER RESULT_CACHE RELIES_ON ( price_list )
IS ret_num NUMBER;
BEGIN
SELECT price INTO ret_num FROM price_list
WHERE item_id = item_id_in
AND from_date <= TRUNC(date_in)
AND to_date > TRUNC(date_in) ;
RETURN ret_num;
END get_price;
END mst;
/
CREATE OR REPLACE PACKAGE mst IS
FUNCTION get_price(item_id_in IN NUMBER, date_in IN DATE)
RETURN NUMBER RESULT_CACHE;
END mst;
/
CREATE OR REPLACE PACKAGE BODY mst IS
FUNCTION get_price(item_id_in IN NUMBER, date_in IN DATE)
RETURN NUMBER RESULT_CACHE RELIES_ON ( price_list )
IS ret_num NUMBER;
BEGIN
SELECT price INTO ret_num FROM price_list
WHERE item_id = item_id_in
AND from_date <= TRUNC(date_in)
AND to_date > TRUNC(date_in) ;
RETURN ret_num;
END get_price;
END mst;
/
キャッシュの有効化
依存する表、Viewの指定
0.00 0.20 0.40 0.60 0.80 1.00 1.20
キャッシュ有り
キャッシュ無し
※ キャッシュ有無で100,000回ループさせた時の差
11g
EE
Copyright© 2009, Oracle. All rights reserved. 14
⑤ PL/SQLのネイティブコンパイル
• PL/SQLのサブプログラムを、プロセッサ固有のネイティブコードにコンパイル、実行することができます。
• PL/SQLサブプログラムを作成する際の、初期化パラメータPLSQL_CODE_TYPE の設定により、ネイティブコンパイルするかどうかが決まる。
- Cコンパイラを利用してコンパイル (要:Cコンパイラ設定) 共有ライブラリの形で利用 (WindowsはDLL、UNIX/Linuxは *.so)- 指定したOS上のディレクトリに共有ライブラリを格納 (要:ディレクトリ指定)
~10gR2
- コンパイラ不要(初期リリースではネイティブコンパイル不可能なプラットフォーム有り)
- SYSTEM表領域上に配置 RAC環境でもバイナリを共有。
11g~
SQL> alter session set plsql_code_type = 'NATIVE';SQL> create or replace procedure …;
Copyright© 2009, Oracle. All rights reserved. 15
ネイティブコンパイルが向くケース
• SQLそのものには効果がありません• SQLでないPL/SQLの演算処理量が多い程効果が高い
• ループ処理や再帰処理などの繰り返しが多い• 文字列演算や数値演算が多い
• 内部でネイティブコンパイルしていないストアド・プログラムを呼び出していると効果は薄い
• 開発環境はデバッグする可能性が高いため、ネイティブコンパイルしない方が良い
Copyright© 2009, Oracle. All rights reserved. 16
ネイティブコンパイルの実行結果
CREATE OR REPLACE PROCEDURE test1 ISTYPE test_tbl_type IS TABLE OF pls_integerINDEX BY pls_integer;
test_tbl test_tbl_type;PROCEDURE test (arg_cnt IN pls_integer,arg_tbl IN OUT NOCOPY test_tbl_type)ISBEGINFOR i IN arg_tbl.FIRST .. arg_tbl.LASTLOOParg_tbl(i) := arg_tbl(i) + i;
END LOOP;END;
-- 隣へ続く
CREATE OR REPLACE PROCEDURE test1 ISTYPE test_tbl_type IS TABLE OF pls_integerINDEX BY pls_integer;
test_tbl test_tbl_type;PROCEDURE test (arg_cnt IN pls_integer,arg_tbl IN OUT NOCOPY test_tbl_type)ISBEGINFOR i IN arg_tbl.FIRST .. arg_tbl.LASTLOOParg_tbl(i) := arg_tbl(i) + i;
END LOOP;END;
-- 隣へ続く
BEGINFOR cnt in 0 .. 10000 LOOPtest_tbl(cnt) := cnt;
END LOOP;FOR cnt in 0 .. 10000 LOOPtest(cnt, test_tbl);
END LOOP;END;/
BEGINFOR cnt in 0 .. 10000 LOOPtest_tbl(cnt) := cnt;
END LOOP;FOR cnt in 0 .. 10000 LOOPtest(cnt, test_tbl);
END LOOP;END;/
exec test1;exec test1;
0.60
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
ネイティブ・コンパイル有
ネイティブ・コンパイル無
Copyright© 2009, Oracle. All rights reserved. 17
<Insert Picture Here>
アジェンダ
• はじめに• パフォーマンス向上の為のPL/SQLの機能紹介
• 言語動作の内部的な動作の「無駄」を省く• 「まとめて」実行する機能を利用する① RETURNING句②バルク処理
• SQLも含めて検討する(SQL新機能紹介)
• パフォーマンスを意識したコーディング
Copyright© 2009, Oracle. All rights reserved. 18
① RETURNING句
• SQLの発行回数を減らすことができる機能• 更新系DML文の操作対象行のうち、指定されたカラムの内容を返す
• 返すデータは複数カラム指定可能
• 返すデータとしてコレクションも指定できる
• 複数件が処理対象になるSQL文でも使える
INSERT INTO … VALUES (…) RETURNING COL1 INTO :COL1;UPDATE … SET … RETURNING COL1 INTO :COL1;DELETE … RETURNING COL1 INTO :COL1;
INSERT INTO … VALUES (…) RETURNING COL1 INTO :COL1;UPDATE … SET … RETURNING COL1 INTO :COL1;DELETE … RETURNING COL1 INTO :COL1;
…… RETURNING COL1, COL2 INTO :COL1, :COL2;…… RETURNING COL1, COL2 INTO :COL1, :COL2;
…… RETURNING COL1 INTO :COL1_ARRAY;…… RETURNING COL1 INTO :COL1_ARRAY;
Copyright© 2009, Oracle. All rights reserved. 19
RETURNING句によるパフォーマンス向上
• 更新系のDML実行と同時に対象行のデータを取得
declareseq number;
begininsert into table1 values(seq1.nextval, 'AAA', 'BBB');select seq1.currval into seq from dual;dbms_output.put_line(seq || 'が採番されました');
end;
declareseq number;
begininsert into table1 values(seq1.nextval, 'AAA', 'BBB');select seq1.currval into seq from dual;dbms_output.put_line(seq || 'が採番されました');
end;
declareseq number;
begininsert into table1 values(seq1.nextval, 'AAA', 'BBB')returning col1 into seq;
dbms_output.put_line(seq || 'が採番されました');
end;
declareseq number;
begininsert into table1 values(seq1.nextval, 'AAA', 'BBB')returning col1 into seq;dbms_output.put_line(seq || 'が採番されました');
end;
上記コードを10万回実行した際のパフォーマンス 0.63
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
RETURNING句使用
RETURNING句不使用
SQLを1回発行SQLを2回発行
Copyright© 2009, Oracle. All rights reserved. 20
②バルク処理 (8i~)
• データ(主にレコード)をまとめて処理する機能• PL/SQLエンジンとSQLエンジンの切り替えを減らすことでパフォーマンスを向上させる• LOOP内でSQLを実行している場合はバルク処理を検討する
PL/SQL エンジン
SQL エンジン
プロシージャ文
エグゼキュータプロシージャPL/SQL
ブロック
SQL文エグゼキュータ
データ
バルク処理なし
PL/SQL エンジン
SQL エンジン
プロシージャ文
エグゼキュータプロシージャPL/SQL
ブロック
SQL文エグゼキュータ
データ
バルク処理あり
SQLSQL
Copyright© 2009, Oracle. All rights reserved. 21
バルク処理(構文)
• FOR ALL (INSERT/UPDATE/DELETE で利用)
• BULK COLLECT(SELECT/FETCH で利用)
FOR i IN depts.FIRST .. depts.LAST LOOPDELETE FROM emp
WHERE dptno = depts(i);END LOOP;
FOR i IN depts.FIRST .. depts.LAST LOOPDELETE FROM emp
WHERE dptno = depts(i);END LOOP;
FORALL i IN depts.FIRST .. depts.LASTDELETE FROM emp
WHERE dptno = depts(i);
FORALL i IN depts.FIRST .. depts.LASTDELETE FROM emp
WHERE dptno = depts(i);
OPEN c1;LOOP
FETCH c1 INTO emp_rec ;EXIT WHEN c1%NOTFOUND;
END LOOP;CLOSE c1;
OPEN c1;LOOP
FETCH c1 INTO emp_rec ;EXIT WHEN c1%NOTFOUND;
END LOOP;CLOSE c1;
OPEN c1;LOOP
FETCH c1 BULK COLLECT INTOemp_rec_tbl LIMIT 200;
EXIT WHEN c1%NOTFOUND;END LOOP;CLOSE c1;
OPEN c1;LOOP
FETCH c1 BULK COLLECT INTOemp_rec_tbl LIMIT 200;
EXIT WHEN c1%NOTFOUND;END LOOP;CLOSE c1;
ループ回数は取得件数と同じ
ループ回数は取得件数の200分の1
バルク処理ありバルク処理なし
バルク処理ありバルク処理なし
Copyright© 2009, Oracle. All rights reserved. 22
FETCH文のバルク処理
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;loopfetch emp_curinto emp_tbl(emp_cur%rowcount);
exit when emp_cur%notfound;end loop;close emp_cur;
end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;loopfetch emp_curinto emp_tbl(emp_cur%rowcount);
exit when emp_cur%notfound;end loop;close emp_cur;
end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;fetch emp_cur bulk collectinto emp_tbl;
close emp_cur;end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;
emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;fetch emp_cur bulk collectinto emp_tbl;
close emp_cur;end;/
10万件で実行した際のパフォーマンス 0.18
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
バルク処理あり
バルク処理なし
Copyright© 2009, Oracle. All rights reserved. 23
SELECT INTO 文のバルク処理
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;loopfetch emp_curinto emp_tbl(emp_cur%rowcount);
exit when emp_cur%notfound;end loop;close emp_cur;
end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;cursor emp_cur is select * from emp;
beginopen emp_cur;loopfetch emp_curinto emp_tbl(emp_cur%rowcount);
exit when emp_cur%notfound;end loop;close emp_cur;
end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;emp_tbl emp_tbl_type;
beginselect * bulk collect into emp_tblfrom emp;
end;/
declaretype emp_tbl_type is table ofemp%rowtype index by pls_integer;
emp_tbl emp_tbl_type;beginselect * bulk collect into emp_tblfrom emp;
end;/
10万件で実行した際のパフォーマンス
0.18
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
バルク処理あり
バルク処理なし
Copyright© 2009, Oracle. All rights reserved. 24
FORALL文によるバルク処理
declaretype emp_tbl_type is table ofemp.empno%type index by pls_integer;emp_tbl emp_tbl_type;
beginselect empno bulk collect into emp_tblfrom emp;for i in emp_tbl.first .. emp_tbl.lastloopupdate emp set sal = sal + 100where empno = emp_tbl(i);
end loop;end;/
declaretype emp_tbl_type is table ofemp.empno%type index by pls_integer;emp_tbl emp_tbl_type;
beginselect empno bulk collect into emp_tblfrom emp;for i in emp_tbl.first .. emp_tbl.lastloopupdate emp set sal = sal + 100where empno = emp_tbl(i);
end loop;end;/
declaretype emp_tbl_type is table ofemp.empno%type index by pls_integer;emp_tbl emp_tbl_type;
beginselect empno bulk collect into emp_tblfrom emp;
forall i in emp_tbl.first.. emp_tbl.lastupdate emp set sal = sal + 100where empno = emp_tbl(i);
end;/
declaretype emp_tbl_type is table ofemp.empno%type index by pls_integer;
emp_tbl emp_tbl_type;beginselect empno bulk collect into emp_tblfrom emp;
forall i in emp_tbl.first.. emp_tbl.lastupdate emp set sal = sal + 100where empno = emp_tbl(i);
end;/
10万件で実行した際のパフォーマンス 0.45
1.00
0.00 0.20 0.40 0.60 0.80 1.00 1.20
バルク処理あり
バルク処理なし
Copyright© 2009, Oracle. All rights reserved. 25
<Insert Picture Here>
アジェンダ
• はじめに• パフォーマンス向上の為のPL/SQLの機能紹介
• 言語動作の内部的な動作の「無駄」を省く• 「まとめて」実行する機能を利用する• SQLも含めて検討する (SQL新機能紹介)① MERGE文② DMLエラー・ロギング
• パフォーマンスを意識したコーディング
Copyright© 2009, Oracle. All rights reserved. 26
① MERGE文(9i R1~)
• MERGE文を使用すると、1つ以上のソースから行を選択し、表またはビューに対して更新および挿入できます
該当レコードがなければ挿入
該当レコードがあれば更新
merge …into
MERGE(UPSERT)
MERGE INTO bonuses D
USING (
SELECT employee_id, salary, department_id
FROM employees
WHERE department_id = 80 ) S
ON (D.employee_id = S.employee_id)
WHEN MATCHED THEN
UPDATE SET D.bonus = D.bonus + S.salary*.01
DELETE WHERE (S.salary > 8000)
WHEN NOT MATCHED THEN
INSERT (D.employee_id, D.bonus)
VALUES (S.employee_id, S.salary*0.1)
WHERE (S.salary <= 8000);
MERGE INTO bonuses D
USING (
SELECT employee_id, salary, department_id
FROM employees
WHERE department_id = 80 ) S
ON (D.employee_id = S.employee_id)
WHEN MATCHED THEN
UPDATE SET D.bonus = D.bonus + S.salary*.01
DELETE WHERE (S.salary > 8000)
WHEN NOT MATCHED THEN
INSERT (D.employee_id, D.bonus)
VALUES (S.employee_id, S.salary*0.1)
WHERE (S.salary <= 8000);
Copyright© 2009, Oracle. All rights reserved. 27
② DMLエラー・ロギング (10g R2~)
• INSERT、UPDATE、MERGE、DELETE文で利用可能• これまでは大量の行を対象とした単一のDMLにエラーが発生すると処理のすべてがロールバックされていました
• 上記DMLに「ERROR LOGS」句をつけることで利用します
• エラー・ロギング表は DBMS_ERRLOG パッケージで作成可能• 実行DMLとエラーロギング表への書き込みトランザクションは分離• DML操作に失敗したデータを記録する為のものであり、すべてのエラーを書き込むものではありません
• 例) ORA-01653(領域不足)、ORA-01555
例) INSERT INTO employees (empno, ename, dptno, sal)( SELECT empno, ename, dptno, sal FROM employees_wk1 )LOG ERRORS INTO ERR$_EMPLOYEES ('WEEKLY_BATCH') REJECT LIMIT 50;
Copyright© 2009, Oracle. All rights reserved. 28
ご参考) エラー・ロギング表の例
INSERT INTO employees (empno, ename, dptno, sal)
( SELECT empno, ename, dptno*1000, sal
FROM employees_old
WHERE empno <= 1)
LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH') REJECT LIMIT 50;
INSERT INTO employees (empno, ename, dptno, sal)
( SELECT empno, ename, dptno*1000, sal
FROM employees_old
WHERE empno <= 1)
LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH') REJECT LIMIT 50;
INSERT でエラー発生 UPDATE でエラー発生対象となるROWID
UPDATE employees
SET dptno = dptno * 1000
WHERE empno = 20
LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH2') REJECT LIMIT 50;
UPDATE employees
SET dptno = dptno * 1000
WHERE empno = 20
LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH2') REJECT LIMIT 50;
故意に桁あふれ
故意に桁あふれ
Copyright© 2009, Oracle. All rights reserved. 29
<Insert Picture Here>
アジェンダ
• はじめに• パフォーマンス向上の為のPL/SQLの機能紹介
• 言語動作の内部的な動作の「無駄」を省く• 「まとめて」実行する機能を利用する• SQLも含めて検討する (SQL新機能紹介)
• MERGE文• DMLエラー・ロギング
• パフォーマンスを意識したコーディング
Copyright© 2009, Oracle. All rights reserved. 30
アプリケーション例での考察 (仕様)
• ワークテーブル(W)の内容をマスターテーブル(M)に反映させる例:• キー項目は同じ• MにWの内容が存在しないデータ(行)はINSERT処理• MにWの内容が存在するデータ(行)はUPDATE処理• エラーが発生した場合に、少なくとも以下の情報を取得
• Oracleのエラー番号、キー項目• エラーが発生しなかったものはすべてMに反映(COMMIT)。大量のエラー(データエラー、システムエラー)が発生した場合、処理を中断(ROLLBACK)
該当レコードがなければ挿入
該当レコードがあれば更新
ワークテーブル(W) マスターテーブル(M)
Copyright© 2009, Oracle. All rights reserved. 31
検証アプリケーションによる比較(前提)
• 検証データ• M(employees)テーブル: 1,000,000件 (平均行長: 26byte)• W(employees_wk1)テーブル: 200,000件 (平均行長: 26byte)
• INSERT対象: 100,000件 (正常系データのみ)• UPDATE対象: 100,000件 (正常系データのみ)
• 比較時の留意事項• M(employees)テーブルはTRUNCATE、INSERTにて測定毎にデータ再作成• M(employees)テーブル再作成後、統計情報取得(dbms_stats.gather_table_stats実施)
• 処理中のREDOログスイッチを避ける為に、事前にログスイッチ• バッファキャッシュにデータ、パッケージがのっていない状態にて実施する為、測定前にDBの再起動を実施
Copyright© 2009, Oracle. All rights reserved. 32
Wの内容をMへ反映(MERGE)処理※ DMLエラー・ロギング機能を利用
ケース5:
1)キー項目の内容がMおよびWに存在するWのデータをそのままMへ反映(UPDATE)処理2)キー項目の内容がMに存在しないWのデータをそのままMへ反映(INSERT)処理※ CURSORを利用しない DMLエラー・ロギング機能を利用
ケース4:
ケース2のバルク処理※ レコードを使用した挿入・更新機能(Appendix参照)を利用
ケース3:
1)キー項目の内容がMおよびWに存在するWのデータをCURSORにて取得。LOOPにてMへ反映(UPDATE)処理2)キー項目の内容がMに存在しないWのデータをCURSORにて取得
LOOPにてMへ反映(INSERT)処理
ケース2:
Wの内容をCURSORで全件取得。LOOPにてMへの反映処理(INSERTを実施し、キー重複エラーが発生した場合にUPDATE実施)
ケース1:
アプリケーション例での考察 (実装案)
一般的な実装
Copyright© 2009, Oracle. All rights reserved. 33
コーディング時のポイント
• バルク処理(BULK COLLECT INTO..)ではLIMIT指定• 利用する結合配列の要素数を100~200程度にすることでメモリを無駄に使わないようにします
• バルク処理(FORALL)ではSAVE EXCEPTIONS指定• エラーが発生した場合でもバルク処理を完了させ、その後エラー処理をまとめておこなうようにします
• 「DMLエラー・ロギング」の機能を使うことでINSERT、UPDATE、MERGE処理をPL/SQLのバッチ処理に組み込みやすくなりました• これまではDML操作に失敗したデータを明確にする為に、
CURSOR+LOOP処理が必須でした
ケース4
ケース5
ケース3
ケース3
Copyright© 2009, Oracle. All rights reserved. 34
ケース1とケース2の性能比較
• ケース1は例外処理を多発させており非常に効率が悪い
0.0149
1.0000
0.0 0.2 0.4 0.6 0.8 1.0 1.2
ケース2
(キー項目有無)
ケース1
(キー重複エラー)
Copyright© 2009, Oracle. All rights reserved. 35
ケース2とケース3の性能比較
• ケース3はケース2のバルク処理対応• ケース2ではLOOP処理がUPDATEで100,000回、INSERTで
100,000回実行されている• ケース3では一度に200件のバルク処理を実施。LOOP処理が
UPDATEで500回、INSERTで500回に削減している
結合配列要素数: 200
結合配列要素数: 100,000
0.4805
1.0000
0.0 0.2 0.4 0.6 0.8 1.0 1.2
ケース3(バルク処理)
ケース2
Copyright© 2009, Oracle. All rights reserved. 36
ケース3のバルク処理に関する考察
• バルク処理にて結合配列にて利用する要素数を変更し、性能差を比較• ケース3では結合配列で利用する要素数を200にしたものと100,000(全件分)にしたものにて実行
• 実行直後のPGAメモリ量(session pga memory)を比較
34,440,260(byte)結合配列要素数(1,000,000):2,524,228(byte)結合配列要素数( 200):
むやみに結合配列の要素数を増やしても、性能が極端に向上するわけではない
※上記のケースでは、メモリを13倍浪費しているにも関わらず、処理時間はむしろ増加している
1.0488
1.0000
0.0 0.2 0.4 0.6 0.8 1.0 1.2
ケース3 (バルク処理)
ケース3
(バルク処理結合配列要素増加)
Copyright© 2009, Oracle. All rights reserved. 37
ケース3、ケース4、ケース5の性能比較
• こちらのいずれかのケースにてコーディングすることになる• ソースコードが簡潔になるのは、ケース4もしくはケース5• DMLエラー・ロギング機能が動作する際の負荷も存在する
0.2886
0.4233
0.4082
0.6455
0.4805
1.0000
0.0 0.2 0.4 0.6 0.8 1.0 1.2
参考:エラーハンドリング無し
参考:エラーハンドリング無し
ケース2 (CURSOR)
ケース3 (CURSOR + バルク処理)
ケース4
(INSERT,UPDATE + ERROR LOG)
ケース4 (INSERT,UPDATE のみ)
ケース5 (MERGE + ERROR LOG)
ケース5 (MERGEのみ)
Copyright© 2009, Oracle. All rights reserved. 38
レコードを利用した挿入・更新+バルク処理DECLARETYPE tbl_emp_rec IS TABLE OF employees%ROWTYPEINDEX BY PLS_INTEGER;TYPE tbl_empno IS TABLE OF employees.EMPNO%TYPEINDEX BY PLS_INTEGER;emp_rec_array tbl_emp_rec;empno_array tbl_empno;CURSOR emp_rec_upd_cur IS SELECT * FROM employees_wk1;BEGINOPEN emp_rec_upd_cur;LOOPemp_rec_array.DELETE; /* 結合配列の初期化 */empno_array.DELETE;FETCH emp_rec_upd_cur BULK COLLECT INTO emp_rec_array LIMIT 200;IF emp_rec_array.COUNT = 0 THENEXIT;END IF;FOR i IN emp_rec_array.FIRST .. emp_rec_array.LAST LOOPempno_array(i) := emp_rec_array(i).empno;END LOOP;FORALL i IN emp_rec_array.FIRST .. emp_rec_array.LASTUPDATE employeesSET ROW = emp_rec_array(i)WHERE empno = empno_array(i);
EXIT WHEN emp_rec_upd_cur%NOTFOUND;END LOOP;CLOSE emp_rec_upd_cur;END;
一度に200件づつ取得
以下のように書きたいが、FORALL処理中に結合配列の要素を参照できない
FORALL i IN emp_rec_array.FIRST .. emp_rec_array.LASTUPDATE employeesSET ROW = emp_rec_array(i)WHERE empno = emp_rec_array(i).empno;
Copyright© 2009, Oracle. All rights reserved. 39
バルク処理(FORALL)のSAVE EXCEPTIONSDECLARETYPE tbl_exception_index IS TABLE OF VARCHAR2(80)INDEX BY PLS_INTEGER;TYPE tbl_exception_errcode IS TABLE OF PLS_INTEGERINDEX BY PLS_INTEGER;err_index_array tbl_exception_index;err_code_array tbl_exception_errcode;ins_errors PLS_INTEGER := 0;err_count PLS_INTEGER := 0;BEGINOPEN … ;
LOOPFETCH …;BEGINFORALL i IN emp_rec_array.FIRST .. emp_rec_array.LAST SAVE EXCEPTIONSINSERT INTO employees VALUES emp_rec_array(i);
EXCEPTIONWHEN OTHERS THENins_errors := ins_errors + SQL%BULK_EXCEPTIONS.COUNT; -- error件数FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT LOOPerr_count := err_count + 1; -- COUNT UPerr_index_array(err_count) := SUBSTRB(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX,1,80);err_code_array(err_count) := SQL%BULK_EXCEPTIONS(i).ERROR_CODE;END LOOP;
END;EXIT WHEN … %NOTFOUND;END LOOP;CLOSE …;END;
例外が発生しても
まとめて処理
Copyright© 2009, Oracle. All rights reserved. 40
DMLエラー・ロギングの利用 (MERGEの例)DECLARE/* 事前に当該スキーマで実行: execute dbms_errlog.create_error_log('EMPLOYEES'); */ins_errors PLS_INTEGER := 0;upd_errors PLS_INTEGER := 0;BEGINMERGE INTO employees eUSING employees_wk3 wON (e.empno = w.empno)WHEN MATCHED THENUPDATESET e.ename = w.ename,e.dptno = w.dptno,e.sal = w.sal
WHEN NOT MATCHED THENINSERT (e.empno, e.ename, e.dptno, e.sal)VALUES (w.empno, w.ename, w.dptno, w.sal)LOG ERRORS INTO ERR$_EMPLOYEES ('WEEKLY_BATCH') REJECT LIMIT 50;COMMIT;
SELECT count(*) INTO ins_errors FROM err$_employeesWHERE ora_err_tag$ = 'WEEKLY_BATCH' AND ora_err_optyp$ = 'I'; /* INSERT */SELECT count(*) INTO upd_errors FROM err$_employeesWHERE ora_err_tag$ = 'WEEKLY_BATCH' AND ora_err_optyp$ = 'U'; /* UPDATE */
EXCEPTIONWHEN OTHERS THENROLLBACK;
END;/
50件以上エラーがあると全体をROLLBACK。
エラーの情報はERR$_EMPLOYEES表に登録される。
アプリ内部でエラー状況を
把握できる
Copyright© 2009, Oracle. All rights reserved. 41
まとめ
•言語動作の内部的な動作の「無駄」を省く
• 「まとめて」実行する機能を利用する
• (PL/SQLだけではなく)SQLも含めて検討する• ロジックを再検討した方が良い場合もある
Copyright© 2009, Oracle. All rights reserved. 42
【タイトル】年末カレンダー応募【必要情報】1、ご登録の氏名2、ご登録の貴社名、所属部署名3、受講された2009年11月・12月開催のセミナタイトル4、現在ご検討中のシステムについてなど、Oracle Directに相談されたい ことなどございましたら記載ください。
年末ダイセミ受講感謝キャンペーン
Oracle Direct Seminarを御愛護頂き、誠にありがとうございます。感謝の気持ちを込めまして、合計100名様にWendy2010年版カレンダーをプレゼントいたします。11月・12月に開催のダイセミを2つ以上受講頂いた方が対象です。是非皆様奮ってご応募下さい!!
応募方法応募方法 [email protected]
必要情報を明記のうえ、メールでご応募ください。当選者の発表は発送をもってかえさせて頂きます。
プレゼントの送付先は、セミナ登録時にご登録されている貴社住所宛てに送付させて頂きます。お客様の登録情報に、a.貴社名、b.部署名、c.役職名、d.住所が正しく登録されていることをご確認ください。a,b,c,dの情報が正しく登録されていない場合はご応募が無効となりますのでご注意下さい。お客様情報の変更はこちらから実施頂けます。
http://www.oracle.com/technology/global/jp/membership/index.html
Copyright© 2009, Oracle. All rights reserved. 43
OTNOTN×ダイセミ でスキルアップ×ダイセミ でスキルアップ!!!!
※OTN掲示版は、基本的にOracleユーザー有志からの回答となるため100%回答があるとは限りません。 ただ、過去の履歴を見ると、質問の大多数に関してなんらかの回答が書き込まれております。
Oracle Technology Network(OTN)を御活用下さい。
・技術的な内容について疑問点を解消したい!・一般的なその解決方法などを知りたい!・セミナ資料など技術コンテンツがほしい!
セミナーに関連する技術的なご質問は、OTN掲示版の「SQLとPL/SQL」へ
http://otn.oracle.co.jp/forum/index.jspa?categoryID=2
過去のセミナ資料、動画コンテンツはOTNの「OTNコンテンツ オン デマンド」へ
http://www.oracle.com/technology/global/jp/ondemand/otn-seminar/index.html
※セミナ事務局にダイセミ資料を請求頂いても、お受けできない可能性がございますので予めご了承ください。 ダイセミ資料はOTNコンテンツ オン デマンドか、セミナ実施時間内にダウンロード頂くようお願い致します。
Copyright© 2009, Oracle. All rights reserved. 44
運用
構築 設計
IT 企画
経営企画
業務改善計画の作成支援• 業務診断サービス• BIアセスメントサービス
システム企画の作成支援•業務診断サービス•BIアセスメントサービス
RFP/提案書の作成支援•BIアセスメントサービス•メインフレーム資産活用相談サービス•仮想化アセスメントサービス•Oracle Database 構成相談サービス•Oracle Database 高可用性クリニック
システム構築時の道案内•Access / SQL Serverからの移行•MySQL / PostgreSQLからの移行•Oracle Database バージョンアップ支援•Oracle Developer Webアップグレード•システム連携アセスメントサービス
システム運用状況の診断•パフォーマンス・クリニック・サービス•システム・セキュリティ診断サービス•データ管理最適化サービス
ITプロジェクト全般に渡る無償支援サービスOracle Direct Conciergeサービスメニュー
Copyright© 2009, Oracle. All rights reserved. 45
http://www.oracle.co.jp/inq_pl/INQUIRY/quest?rid=28
Oracle Direct 検索
あなたにいちばん近いオラクル
Oracle Directまずはお問合せくださいまずはお問合せください
Web問い合わせフォーム フリーダイヤル
専用お問い合わせフォームにてご相談内容を承ります。
※フォームの入力には、Oracle Direct Seminar申込時と同じ ログインが必要となります。※こちらから詳細確認のお電話を差し上げる場合がありますので、ご登録さ れている連絡先が最新のものになっているか、ご確認下さい。
0120-155-096 ※月曜~金曜 9:00~12:00、13:00~18:00
(祝日および年末年始除く)
システムの検討・構築から運用まで、ITプロジェクト全般の相談窓口としてご支援いたします。
システム構成やライセンス/購入方法などお気軽にお問い合わせ下さい。
Copyright© 2009, Oracle. All rights reserved. 46
Appendix
Copyright© 2009, Oracle. All rights reserved. 47
ケース1のソース(主要ロジック部分のみ)①
CREATE OR REPLACE PACKAGE d01
IS
PROCEDURE wrk_to_master;
END d01;
/
show errors
CREATE OR REPLACE PACKAGE BODY d01
IS
emp_rec employees%ROWTYPE;
CURSOR emp_rec_cur IS
SELECT empno,ename,dptno,sal
FROM employees_wk1
ORDER BY empno;
PROCEDURE wrk_to_master
IS
BEGIN
OPEN emp_rec_cur;
LOOP
FETCH emp_rec_cur INTO emp_rec;
EXIT WHEN emp_rec_cur%NOTFOUND;
BEGIN
INSERT INTO employees (
empno,
ename,
dptno,
sal
)VALUES(
emp_rec.empno,
emp_rec.ename,
emp_rec.dptno,
emp_rec.sal
);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
BEGIN
UPDATE employees
SET ename = emp_rec.ename,
dptno = emp_rec.dptno,
sal = emp_rec.sal
WHERE empno = emp_rec.empno;
EXCEPTION
WHEN OTHERS THEN
/* update error */
NULL; /* 本来はエラー処理 */
END;
Copyright© 2009, Oracle. All rights reserved. 48
ケース1のソース(主要ロジック部分のみ)②
WHEN OTHERS THEN
/* insert error */
NULL; /* 本来はエラー処理 */
END;
END LOOP;
CLOSE emp_rec_cur;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
/* error */
IF emp_rec_cur%ISOPEN THEN
CLOSE emp_rec_cur;
END IF;
ROLLBACK;
RAISE;
END wrk_to_master;
END d01;
/
show errors
Copyright© 2009, Oracle. All rights reserved. 49
ケース2のソース(主要ロジック部分のみ)①
CREATE OR REPLACE PACKAGE d02
IS
PROCEDURE wrk_to_master;
END d02;
/
show errors
CREATE OR REPLACE PACKAGE BODY d02
IS
emp_rec employees%ROWTYPE;
CURSOR emp_rec_upd_cur IS
SELECT w.empno, w.ename, w.dptno, w.sal
FROM employees_wk1 w
WHERE EXISTS (
SELECT 'X' FROM employees e
WHERE w.empno = e.empno
)
ORDER BY w.empno;
CURSOR emp_rec_ins_cur IS
SELECT w.empno, w.ename, w.dptno, w.sal
FROM employees_wk1 w
WHERE NOT EXISTS (
SELECT 'X' FROM employees e
WHERE w.empno = e.empno
)
ORDER BY w.empno;
PROCEDURE wrk_to_master
IS
BEGIN
/* まずキー重複しているものをUPDATE */
OPEN emp_rec_upd_cur;
LOOP
FETCH emp_rec_upd_cur INTO emp_rec;
EXIT WHEN emp_rec_upd_cur%NOTFOUND;
BEGIN
UPDATE employees
SET ename = emp_rec.ename,
dptno = emp_rec.dptno,
sal = emp_rec.sal
WHERE empno = emp_rec.empno;
EXCEPTION
WHEN OTHERS THEN
NULL; /* 本来はエラー処理 */
END;
END LOOP;
CLOSE emp_rec_upd_cur;
Copyright© 2009, Oracle. All rights reserved. 50
ケース2のソース(主要ロジック部分のみ)②
/* 次にキー重複していないものをINSERT */
OPEN emp_rec_ins_cur;
LOOP
FETCH emp_rec_ins_cur INTO emp_rec;
EXIT WHEN emp_rec_ins_cur%NOTFOUND;
BEGIN
INSERT INTO employees (
empno,
ename,
dptno,
sal
)VALUES(
emp_rec.empno,
emp_rec.ename,
emp_rec.dptno,
emp_rec.sal
);
EXCEPTION
WHEN OTHERS THEN
NULL; /* 本来はエラー処理 */
END;
END LOOP;
CLOSE emp_rec_ins_cur;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
/* error */
IF emp_rec_upd_cur%ISOPEN THEN
CLOSE emp_rec_upd_cur;
END IF;
IF emp_rec_ins_cur%ISOPEN THEN
CLOSE emp_rec_ins_cur;
END IF;
ROLLBACK;
RAISE;
END wrk_to_master;
END d02;
/
show errors
Copyright© 2009, Oracle. All rights reserved. 51
ケース3のソース(主要ロジック部分のみ)①
CREATE OR REPLACE PACKAGE d03
IS
PROCEDURE wrk_to_master;
END d03;
/
show errors
CREATE OR REPLACE PACKAGE BODY d03
IS
TYPE tbl_emp_rec IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
TYPE tbl_empno IS TABLE OF employees.EMPNO%TYPE
INDEX BY PLS_INTEGER;
emp_rec_array tbl_emp_rec;
empno_array tbl_empno;
CURSOR emp_rec_upd_cur IS
SELECT w.empno, w.ename, w.dptno, w.sal
FROM employees_wk1 w
WHERE EXISTS (
SELECT 'X' FROM employees e
WHERE w.empno = e.empno
)
ORDER BY w.empno;
CURSOR emp_rec_ins_cur IS
SELECT w.empno, w.ename, w.dptno, w.sal
FROM employees_wk1 w
WHERE NOT EXISTS (
SELECT 'X' FROM employees e
WHERE w.empno = e.empno
)
ORDER BY w.empno;
PROCEDURE wrk_to_master
IS
BEGIN
/* まずキー重複しているものをUPDATE */
OPEN emp_rec_upd_cur;
LOOP
emp_rec_array.DELETE; /* 結合配列の初期化 */
empno_array.DELETE;
FETCH emp_rec_upd_cur BULK COLLECT
INTO emp_rec_array LIMIT 200;
IF emp_rec_array.COUNT = 0 THEN
EXIT;
END IF;
FOR i IN emp_rec_array.FIRST ..
emp_rec_array.LAST LOOP
empno_array(i) := emp_rec_array(i).empno;
END LOOP;
Copyright© 2009, Oracle. All rights reserved. 52
ケース3のソース(主要ロジック部分のみ)②
BEGIN
FORALL i IN emp_rec_array.FIRST ..
emp_rec_array.LAST SAVE EXCEPTIONS
UPDATE employees
SET ROW = emp_rec_array(i)
WHERE empno = empno_array(i);
EXCEPTION
WHEN OTHERS THEN
NULL; /* 本来はエラー処理 */
END;
EXIT WHEN emp_rec_upd_cur%NOTFOUND;
END LOOP;
CLOSE emp_rec_upd_cur;
/* 次にキー重複していないものをINSERT */
OPEN emp_rec_ins_cur;
LOOP
emp_rec_array.DELETE; /* 結合配列の初期化 */
FETCH emp_rec_ins_cur BULK COLLECT INTO
emp_rec_array LIMIT 200;
BEGIN
FORALL i IN emp_rec_array.FIRST ..
emp_rec_array.LAST SAVE EXCEPTIONS
INSERT INTO employees
VALUES emp_rec_array(i);
EXCEPTION
WHEN OTHERS THEN
NULL; /* 本来はエラー処理 */
END;
EXIT WHEN emp_rec_ins_cur%NOTFOUND;
END LOOP;
CLOSE emp_rec_ins_cur;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
/* error */
IF emp_rec_upd_cur%ISOPEN THEN
CLOSE emp_rec_upd_cur;
END IF;
IF emp_rec_ins_cur%ISOPEN THEN
CLOSE emp_rec_ins_cur;
END IF;
ROLLBACK;
RAISE;
END wrk_to_master;
END d03;
/
show errors
Copyright© 2009, Oracle. All rights reserved. 53
ケース4のソース(主要ロジック部分のみ)①
CREATE OR REPLACE PACKAGE d04
IS
PROCEDURE wrk_to_master;
END d04;
/
show errors
CREATE OR REPLACE PACKAGE BODY d04
IS
/* 事前に、エラー・ロギング表を作成しておく */
PROCEDURE wrk_to_master
IS
BEGIN
/* まずキー重複しているものをUPDATE */
UPDATE employees e
SET (ename, dptno, sal) = (
SELECT w.ename, w.dptno, w.sal
FROM employees_wk1 w
WHERE w.empno = e.empno
)
WHERE EXISTS (
SELECT 'X' FROM employees_wk1 wk
WHERE e.empno = wk.empno
) LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH') REJECT LIMIT 50;
/* 次にキー重複していないものをINSERT */
INSERT INTO employees (empno,ename,dptno,sal) (
SELECT w.empno, w.ename, w.dptno, w.sal
FROM employees_wk1 w WHERE NOT EXISTS (
SELECT 'X' FROM employees e
WHERE e.empno = w.empno
)
) LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH') REJECT LIMIT 50;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END wrk_to_master;
END d04;
/
show errors
Copyright© 2009, Oracle. All rights reserved. 54
ケース5のソース(主要ロジック部分のみ)①
CREATE OR REPLACE PACKAGE d05
IS
PROCEDURE wrk_to_master;
END d05;
/
show errors
CREATE OR REPLACE PACKAGE BODY d05
IS
/* 事前に、エラー・ロギング表を作成しておく */
PROCEDURE wrk_to_master
IS
BEGIN
MERGE INTO employees e
USING employees_wk1 w
ON (e.empno = w.empno)
WHEN MATCHED THEN
UPDATE
SET e.ename = w.ename,
e.dptno = w.dptno,
e.sal = w.sal
WHEN NOT MATCHED THEN
INSERT (e.empno, e.ename, e.dptno, e.sal)
VALUES (w.empno, w.ename, w.dptno, w.sal)
LOG ERRORS INTO ERR$_EMPLOYEES
('WEEKLY_BATCH') REJECT LIMIT 50;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END wrk_to_master;
END d05;
/
show errors
Copyright© 2009, Oracle. All rights reserved. 55
レコードを利用した挿入と更新
• 9iR1以前DECLARE
emp_rec employees%ROWTYPE;
BEGIN
emp_rec.empno := 99;
emp_rec.ename := '従業員名';
emp_rec.dptno := 10;
INSERT INTO employees (empno, ename, dptno)
VALUES (emp_rec.empno,
emp_rec.ename,
emp_rec.dptno);
END;
/
DECLARE
emp_rec employees%ROWTYPE;
BEGIN
emp_rec.empno := 99;
emp_rec.ename := '会社員名';
emp_rec.dptno := 20;
UPDATE employees
SET ename = emp_rec.ename,
dptno = emp_rec.dptno
WHERE empno = emp_rec.empno;
END;
/
INSERT文 UPDATE文
• 9iR2以後INSERT文 UPDATE文DECLARE
emp_rec employees%ROWTYPE;
BEGIN
emp_rec.empno := 99;
emp_rec.ename := '従業員名';
emp_rec.dptno := 10;
INSERT INTO employees VALUES emp_rec;
END;
/
DECLARE
emp_rec employees%ROWTYPE;
BEGIN
emp_rec.empno := 99;
emp_rec.ename := '会社員名';
emp_rec.dptno := 20;
UPDATE employees
SET ROW = emp_rec
WHERE empno = emp_rec.empno;
END;
/
INSERT文 UPDATE文
9iR2
Copyright© 2009, Oracle. All rights reserved. 56
CONTINUE 文
• LOOP制御にCONTINUE文を利用できるようになりました。• CONTINUE文でLOOPの現在処理中の反復を終了し、次のLOOP処理の反復に制御を移します。
• CONTINUE と CONTINUE-WHEN があります。• EXIT(LOOPの終了)と同様、ラベルも使えます。
DECLARE
BEGIN
<<FIRST_LOOP>>
FOR i IN 1 .. 5 LOOP
<<SECOND_LOOP>>
FOR k IN 1 .. 5 LOOP
CASE k
WHEN 2 THEN CONTINUE SECOND_LOOP;
WHEN 4 THEN CONTINUE FIRST_LOOP;
ELSE NULL;
END CASE;
DBMS_OUTPUT.PUT_LINE('i,k: '||TO_CHAR(i)||','||TO_CHAR(k));
END LOOP; /* k */
END LOOP; /* i */
END;
/
DECLARE
BEGIN
<<FIRST_LOOP>>
FOR i IN 1 .. 5 LOOP
<<SECOND_LOOP>>
FOR k IN 1 .. 5 LOOP
CONTINUE SECOND_LOOP WHEN k = 2;
CONTINUE FIRST_LOOP WHEN k = 4;
DBMS_OUTPUT.PUT_LINE('i,k: '||TO_CHAR(i)||','||TO_CHAR(k));
END LOOP; /* k */
END LOOP; /* i */
END;
/
CONTINUE CONTINUE-WHEN
11g
Copyright© 2009, Oracle. All rights reserved. 57
PL/SQL式での sequence の記述
• これまでは SELECT INTO 文にて sequence の値を取得する必要がありましたが、PL/SQL内にて直接値を取得できるようになりました。• 事前に sequenceオブジェクトの定義が必要です。DECLARE
seq_num NUMBER(8);
BEGIN
SELECT tst_seq.nextval INTO seq_num
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('SEQ: '
||TO_CHAR(seq_num));
END;
/
DECLARE
seq_num NUMBER(8);
BEGIN
seq_num := tst_seq.nextval;
DBMS_OUTPUT.PUT_LINE('SEQ: '
||TO_CHAR(seq_num));
END;
/
~ 10gR2 11g ~
11g
Copyright© 2009, Oracle. All rights reserved. 58
PL/SQLサブプログラムの引数の名前表記、混合表記
• デフォルト値を設定しているPL/SQLのファンクションをSQL文中で利用する際に、引数を名前表記、混合表記できるようになりました。• デフォルト値を多く持つファンクションが使いやすくなります。
SELECT compute_bonus(120, 50) FROM dual;
~ 10gR2:位置表記法のみ
SELECT compute_bonus(emp_id => 120, bonus => 50) FROM dual;
名前表記法
SELECT compute_bonus(120, bonus => 50) FROM dual;
混合表記法
11g
Copyright© 2009, Oracle. All rights reserved. 59
複合トリガー
• 複数のタイミングで起動できるDMLトリガーCREATE OR REPLACE TRIGGER compound_triggerFOR UPDATE OF salary ON employeesCOMPOUND TRIGGER-- Declarative part (optional)BEFORE STATEMENT ISBEGINNULL;END BEFORE STATEMENT;
BEFORE EACH ROW ISBEGINNULL;END BEFORE EACH ROW;
AFTER EACH ROW ISBEGINNULL;END AFTER EACH ROW;
AFTER STATEMENT ISBEGINNULL;END AFTER STATEMENT;END compound_trigger;
CREATE OR REPLACE TRIGGER compound_triggerFOR UPDATE OF salary ON employeesCOMPOUND TRIGGER-- Declarative part (optional)BEFORE STATEMENT ISBEGINNULL;END BEFORE STATEMENT;
BEFORE EACH ROW ISBEGINNULL;END BEFORE EACH ROW;
AFTER EACH ROW ISBEGINNULL;END AFTER EACH ROW;
AFTER STATEMENT ISBEGINNULL;END AFTER STATEMENT;END compound_trigger;
•利用が効果的なケース
1. 行単位で処理を行うが、処理そのものはまとめて実施したい
例:ある表への更新情報を別表へ挿入
- AFTER EACH ROW 配列に情報を保持
- AFTER STATEMENT 配列の情報を別表にまとめて挿入
例:ある表のカラムAの平均値を超える更新を許可しない- BEFORE STATEMENT カラムAの平均値を取得- AFTER EACH ROW 更新値とカラムAの平均値を比較し、 平均値を超えていたらエラー
2. 変更表エラー(ORA-04091 )の回避
11g
Copyright© 2009, Oracle. All rights reserved. 60
<Insert Picture Here>
PL/SQLのデバッグ
Copyright© 2009, Oracle. All rights reserved. 61
PL/SQLのデバッグ方法• DBMS_OUTPUTで値をチェック
• 古いバージョンでも使えるけど原始的• DBMS_DEBUGパッケージ
• PL/SQLアプリケーションの実行をデバッグするためのAPI• ブレークポイントの設定、トレース実行など一通りのデバッグが可能• 使い方が難しい→GUIのPL/SQL開発・デバッグツールを推奨
• GUIのPL/SQL開発・デバッグツール• Oracle JDeveloper• Oracle SQL Developer• Oracle Developer Tools for Visual Studio .NET
Copyright© 2009, Oracle. All rights reserved. 62
<Insert Picture Here>
SQL Developerによるデバッグ
デバッグ対象は、ストアド・プログラムのみ
Copyright© 2009, Oracle. All rights reserved. 63
PL/SQLデバッグ (対象を選択)
ダブルクリック
エディットモードへ
Copyright© 2009, Oracle. All rights reserved. 64
PL/SQLデバッグ(デバッグ用コンパイル)
デバッグ用にコンパイル
行番号表示
右クリックでメニュー表示
Copyright© 2009, Oracle. All rights reserved. 65
PL/SQLデバッグ(ブレイク・ポイントの設定)
左クリックでブレイク・ポイント設定
PL/SQLブロックの折りたたみ可能
Copyright© 2009, Oracle. All rights reserved. 66
PL/SQLデバッグ (開始時点)
デバッグボタンを押すと、デバッグ開始画面
パラメータを設定して「OK」ボタンを押す
Copyright© 2009, Oracle. All rights reserved. 67
デバッグで利用する画面
PL/SQLデバッグ (OKボタン押下直後)
最初のブレイク・ポイントで停止
Copyright© 2009, Oracle. All rights reserved. 68
PL/SQLデバッグ (変数の値の参照・値の変更)
- カウンターで使っている変数 I が3にカウントアップ直後。
-配列はまだ2つしか値がセットされていない。
ステップ毎に実行
Copyright© 2009, Oracle. All rights reserved. 69
<Insert Picture Here>
PL/SQLのプロファイリング
Copyright© 2009, Oracle. All rights reserved. 70
PL/SQLアプリのプロファイリング• プロファイリングとは?
• プログラムの稼動情報を収集し、分析すること• 実行時間• CPU時間• メモリ消費
• パフォーマンス・ボトルネックの調査などに利用• PL/SQLアプリのプロファイリング
• DBMS_PROIFILERパッケージを利用• 各行の実行時間及び実行回数のみ取得可
Oracle8i~
Copyright© 2009, Oracle. All rights reserved. 71
事前準備
1. SYSユーザーで$ORACLE_HOME/rdbms/admin/profload.sqlを実行
- DBMS_PROIFILERパッケージのインストール
2. DBMS_PROIFILERパッケージ実行ユーザーで$ORACLE_HOME/rdbms/admin/proftab.sqlを実行
- DBMS_PROIFILERが使用するテーブルを作成• PLSQL_PROFILER_RUNS
プロファイリングの実行に関する情報を格納
• PLSQL_PROFILER_UNITSプロファイリング対象ユニットに関する情報を格納
• PLSQL_PROFILER_DATAプロファイリングデータを格納
Copyright© 2009, Oracle. All rights reserved. 72
DBMS_PROFILERの使い方1. DBMS_PROFILER.START_PROFILERを実行してプロファイリング開始
- SQL> execute dbms_profiler.start_profiler ('PROFILING SAMPLE1')
2. プロファイリング対象アプリを実行3. DBMS_PROFILER.STOP_PROFILERを実行してプロファイリング終了
- SQL> execute dbms_profiler.stop_profiler
4. プロファイリング情報テーブルを検索- PLSQL_PROFILER_RUNS
- プロファイリング対象アプリの実行ID(RUNID列)を確認- PLSQL_PROFILER_DATA
- プロファイル内容を確認
実行コメントを入れられる
Copyright© 2009, Oracle. All rights reserved. 73
DBMS_PROFILER使用例SQL> execute dbms_profiler.start_profiler ('number_test.' || to_char(sysdate, 'yyyy/mm/dd hh24:mi:ss'))
PL/SQL procedure successfully completed.
SQL> execute number_test
PL/SQL procedure successfully completed.
SQL> execute dbms_profiler.stop_profiler
PL/SQL procedure successfully completed.
SQL> select runid, run_date, run_comment2 from plsql_profiler_runs order by runid;
RUNID RUN_DATE---------- ---------RUN_COMMENT--------------------------------------------------------------------------------
1 20-APR-04number_test.2004/04/20 18:45:56
2 20-APR-04number_test.2004/04/20 18:48:12
プロファイル対象のレコードRUNID=2に注目
プロファイリング開始
プロファイリング対象アプリ実行
プロファイリング終了-- 実行サンプルソースcreate or replace procedure number_test isn number := 1;b binary_integer := 1;p pls_integer := 1;dummy varchar2(20);begindummy := 'start of plsql';for i in 1 .. 100 loopn := n + 1;b := b + 1;p := p + 1;dummy := 'counts for
endloop';end loop;dummy := 'end of plsql';end;
Copyright© 2009, Oracle. All rights reserved. 74
DBMS_PROFILER使用例(続き)SQL> select p.unit_name, p.occured, p.tot_time, p.line# line,2 substr(s.text, 1,75) text3 from4 (select u.unit_name, d.TOTAL_OCCUR occured,5 (d.TOTAL_TIME/1000000000) tot_time, d.line#6 from plsql_profiler_units u, plsql_profiler_data d7 where d.RUNID=u.runid and d.UNIT_NUMBER = u.unit_number8 and d.TOTAL_OCCUR >09 and u.runid = 2) p,10 user_source s11 where p.unit_name = s.name(+) and p.line# = s.line (+)12 order by p.unit_name, p.line#;
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';
9行目:RUNIDを入力(例だと2)
単位:ナノ秒
Copyright© 2009, Oracle. All rights reserved. 75
以上の事項は、弊社の一般的な製品の方向性に関する概要を説明するものです。また、情報提供を唯一の目的とするものであり、いかなる契約にも組み込むことはできません。以下の事項は、マテリアルやコード、機能を提供することをコミットメント(確約)するものではないため、購買決定を行う際の判断材料になさらないで下さい。オラクル製品に関して記載されている機能の開発、リリースおよび時期については、弊社の裁量により決定されます。
Oracle、PeopleSoft、JD Edwards、及びSiebelは、米国オラクル・コーポレーション及びその子会社、関連会社の登録商標です。その他の名称はそれぞれの会社の商標の可能性があります。