Oracle PL/SQL入門
- PL/SQLについて
- 宣言部(DECLARE)の変数などの宣言
- 変数への代入(値設定)
- プログラム実行制御(IF文、CASE文、GOTO文)
- プログラム繰返し制御(LOOP文、WHILE...LOOP文、FOR...LOOP文)
- カーソルについて
- 例外処理について(EXCEPTION)
おすすめ書籍
- 図解入門よくわかる最新Oracleデータベースの基本と仕組み[第4版] (How‐nual Visual Guide Book)
- Oracleの基本 ~データベース入門から設計/運用の初歩まで
- [改訂第4版]SQLポケットリファレンス
PL/SQLについて
PL/SQL とはOracle社がOracle Databaseのためにデータベース問合せ言語である SQL を独自に拡張した手続き型プログラミング言語です。
PL/SQL を使うと SQL のみでは1回のデータ処理しかできないものを、その SQL の繰返しやデータの値による条件判断により
きめ細かいデータ処理ができる様になります。
PL/SQL の基本的な構造は以下の様になります。
DECLARE の{変数、定数、カーソル等の宣言部}は無くてもいいのですが、この{処理実行部}で必要な変数等の宣言を行います。
BEGIN ... END; の{処理実行部}に実行させたい命令やSQL文等を記述します。
また、例外処理を行う場合には BEGIN ... END; の中に EXCEPTION を記述し EXCEPTION ... END; の間に{例外処理部}を記述します。
[DECLARE
{変数、定数、カーソル等の宣言部}
]
BEGIN
{処理実行部}
[EXCEPTION
{例外処理部}
]
END;
([ ... ] は省略可を表す)
以下にSQLPLUS上で実行した PL/SQL の実行例を示します。
この例では、1個の変数を宣言し、実行部ではその変数に文字列を設定し
DBMS_OUTPUT.PUT_LINE により標準出力への表示を行います。
その後、SQL文によりシステム日付の文字列化したものを変数に設定し、標準出力への表示を行います。
PL/SQL の実行の前にSET SERVEROUTPUT ON を行いますが、
これにより DBMS_OUTPUT.PUT_LINE がSQLPLUS上に表示される様にします。
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 -- 変数宣言
3 WORK VARCHAR2(32);
4 BEGIN
5 -- 単純に変数への設定
6 WORK := '1234567890';
7 DBMS_OUTPUT.PUT_LINE('TEST=' || WORK);
8 -- 変数へシステム日付の設定
9 SELECT TO_CHAR(SYSDATE, 'YYYY/MM/DD') INTO WORK FROM DUAL;
10 DBMS_OUTPUT.PUT_LINE('DATE=' || WORK);
11 END;
12 /
TEST=1234567890
DATE=2018/12/13
PL/SQLプロシージャが正常に完了しました。
SQL>
宣言部(DECLARE)の変数などの宣言
データ型の変数宣言を行う場合は以下の様にします。変数名の後にデータ型を記述します。初期値がある場合は :=(コロン、イコール) で結びます。
<変数名> <データ型> [:= <初期値>];
各種のデータ型の変数宣言の例と、%TYPEを使った宣言及び、カーソル宣言の例を以下に記します。
%TYPEを使った宣言はテーブルのカラム名に%TYPEを付加して宣言します。
宣言された変数は、テーブルのカラムのデータ型と同じものを宣言したことになります。
テーブル由来の宣言とすることで、テーブルのカラム型の桁数を変えた時にはソース上の変更なしに再度実行で反映されます。
カーソル宣言はテーブルから順次レコードを取得する場合に使用されるものですが、後に詳しく説明します。
尚、USER_SOURCE テーブルの定義の表示を合わせて行っています。
SQL> DECLARE 2 -- 変数宣言 3 -- VARCHAR2型宣言 4 wk_vc1 VARCHAR2(32); 5 wk_vc2 VARCHAR2(32) := '111111'; 6 7 -- NUMBER型宣言 8 wk_nm1 NUMBER; 9 wk_nm2 NUMBER(9); 10 wk_nm3 NUMBER(9, 3); -- 小数点以下3桁で全部で9桁 11 12 -- DATE型宣言 13 wk_dt1 DATE; 14 15 -- %TYPEを使った宣言(ユーザ関数が登録されているテーブル:USER_SOURCE から) 16 wk_type1 USER_SOURCE.NAME%TYPE; -- NUMBER の宣言と同様 17 wk_type2 USER_SOURCE.LINE%TYPE; -- VARCHAR2(4000) の宣言と同様 18 19 -- カーソル宣言(テーブル:USER_SOURCE から) 20 CURSOR wk_cur1 IS 21 SELECT LINE, TEXT 22 FROM USER_SOURCE; 23 24 BEGIN 25 -- 何もしない命令 26 NULL; 27 END; 28 / PL/SQLプロシージャが正常に完了しました。 SQL> SQL> DESC USER_SOURCE 名前 NULL? 型 ----------------------------------------- -------- ---------------------------- NAME VARCHAR2(30) TYPE VARCHAR2(12) LINE NUMBER TEXT VARCHAR2(4000)
変数への代入(値設定)
SQL> DECLARE
2 -- VARCHAR2型宣言
3 wk_vc1 VARCHAR2(32);
4 wk_vc2 VARCHAR2(32) := '111111';
5 -- NUMBER型宣言
6 wk_nm1 NUMBER;
7 wk_nm2 NUMBER(9);
8 BEGIN
9 -- 変数へのリテラルの代入
10 wk_vc1 := 'abcdefg';
11 wk_nm1 := 1000;
12 DBMS_OUTPUT.PUT_LINE('wk_vc1=' || wk_vc1);
13 DBMS_OUTPUT.PUT_LINE('wk_nm1=' || TO_CHAR(wk_nm1));
14 -- 変数から変数への代入
15 wk_vc2 := wk_vc1;
16 wk_nm2 := wk_nm1;
17 DBMS_OUTPUT.PUT_LINE('wk_vc2=' || wk_vc2);
18 DBMS_OUTPUT.PUT_LINE('wk_nm2=' || TO_CHAR(wk_nm2));
19 -- SELECT文による変数への代入
20 SELECT TO_CHAR(SYSDATE, 'YYYY/MM/DD'), TO_CHAR(SYSDATE, 'HH24:MI:SS')
21 INTO wk_vc1, wk_vc2 FROM DUAL;
22 DBMS_OUTPUT.PUT_LINE('DATE=' || wk_vc1);
23 DBMS_OUTPUT.PUT_LINE('TIME=' || wk_vc2);
24 END;
25 /
wk_vc1=abcdefg
wk_nm1=1000
wk_vc2=abcdefg
wk_nm2=1000
DATE=2018/12/13
TIME=18:51:48
PL/SQLプロシージャが正常に完了しました。
SQL>
プログラム実行制御(IF文、CASE文、GOTO文)
プログラムの実行を制御する文として IF文、CASE文、GOTO文等があります。
変数の値により条件を判断し処理の振り分けを行います。先ずは IF文 の構造ですが、以下の様になります。
他のプログラム言語と大差は無いと思います。
但し普通の言語だと ELSEIF となるところが ELSIF と「E」が抜けていのと、
最後の END IF の END と IF の間の空白には注意が必要です。
IF文、CASE文の書式
-- IF文の書式について
IF <条件式1> THEN
<処理文1>;
[ELSIF <条件式2> THEN
<処理文2>;]
...
[ELSIF <条件式n> THEN
<処理文n>;]
[ELSE
<処理文>;]
END IF;
-- CASE文の書式について
CASE
WHEN <判定値1> THEN
<処理文1>;
[WHEN <判定値2> THEN
<処理文2>;]
...
[WHEN <判定値n> THEN
<処理文n>;]
ELSE -- ELSE文は必須!!
<処理文>;
END CASE;
-- CASE文の書式についてその2(検索CASE文)
CASE
WHEN <判定式1> THEN
<処理文1>;
[WHEN <判定式2> THEN
<処理文2>;]
...
[WHEN <判定式n> THEN
<処理文n>;]
ELSE
<処理文>;
END CASE;
以下に IF文 CASE文 検索CASE文 の例を示します。
IF文 CASE文 検索CASE文 の例
SQL> DECLARE
2 -- VARCHAR2型宣言
3 wk_vc1 VARCHAR2(32);
4 BEGIN
5 -- 仮に変数へのリテラルの代入
6 wk_vc1 := 'a';
7
8 -- IF文
9 IF wk_vc1 = 'a' THEN
10 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a" です。');
11 ELSIF wk_vc1 = 'b' THEN
12 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "b" です。');
13 ELSIF wk_vc1 = 'c' THEN
14 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "c" です。');
15 ELSE
16 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a","b","c" 以外です。');
17 END IF;
18
19 wk_vc1 := 'b';
20 -- CASE文
21 CASE wk_vc1
22 WHEN 'a' THEN
23 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a" です。');
24 WHEN 'b' THEN
25 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "b" です。');
26 WHEN 'c' THEN
27 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "c" です。');
28 ELSE
29 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a","b","c" 以外です。');
30 END CASE;
31
32 wk_vc1 := 'abc';
33 -- 検索CASE文
34 CASE
35 WHEN wk_vc1 = 'a' THEN
36 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a" です。');
37 WHEN wk_vc1 = 'b' THEN
38 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "b" です。');
39 WHEN wk_vc1 = 'c' THEN
40 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "c" です。');
41 ELSE
42 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a","b","c" 以外です。');
43 END CASE;
44 END;
45 /
wk_vc1の値は "a" です。
wk_vc1の値は "b" です。
wk_vc1の値は "a","b","c" 以外です。
PL/SQLプロシージャが正常に完了しました。
SQL>
以下に GOTO文 の例を示しますが、余り使用頻度は高くないと思います。
GOTO文でどの位置でもジャンプできるのですが、使い道としては多重ループからの抜出し等でしょうか。
なるべく IF文 CASE文 で処理するべきと思います。
GOTO文 の例
SQL> DECLARE
2 -- VARCHAR2型宣言
3 wk_vc1 VARCHAR2(32);
4 BEGIN
5 -- 仮に変数へのリテラルの代入
6 wk_vc1 := 'a';
7
8 -- IF文
9 IF wk_vc1 = 'a' THEN
10 GOTO jump_a;
11 ELSIF wk_vc1 = 'b' THEN
12 GOTO jump_b;
13 ELSIF wk_vc1 = 'c' THEN
14 GOTO jump_c;
15 END IF;
16
17 GOTO jump_end;
18
19 <<jump_a>>
20 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "a" です。');
21 GOTO jump_end;
22
23 <<jump_b>>
24 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "b" です。');
25 GOTO jump_end;
26
27 <<jump_c>>
28 DBMS_OUTPUT.PUT_LINE('wk_vc1の値は "c" です。');
29 -- GOTO jump_end;
30
31 <<jump_end>>
32 DBMS_OUTPUT.PUT_LINE('プロシージャが終了しました。');
33 END;
34 /
wk_vc1の値は "a" です。
プロシージャが終了しました。
PL/SQLプロシージャが正常に完了しました。
SQL>
プログラム繰返し制御(LOOP文、WHILE...LOOP文、FOR...LOOP文)
プログラムの繰返し用の制御文として、無限ループ用の LOOP文、繰返しの条件付きループ用の WHILE...LOOP文、
繰返し回数を指定してのループ用の FOR...LOOP文があります。
以下にそれぞれの書式を記します。
LOOP文、WHILE...LOOP文、FOR...LOOP文の書式
-- LOOP文の書式について
LOOP
<処理文>;
-- ループを抜出す場合
[ IF <条件式> THEN
EXIT;
END IF; ]
-- ループを抜出す場合その2
[ EXIT WHEN <条件式>; ]
END LOOP;
-- WHILE...LOOP文の書式について
WHILE <条件式> LOOP
<処理文>;
END LOOP;
-- FOR...LOOP文の書式について
FOR <ループカウンタ> IN [ REVERSE ] <初期値>..<終了値> LOOP
<処理文>;
-- 以下の処理をスキップする場合
[ CONTINUE WHEN <条件式>;
<処理文2>; ]
END LOOP;
無限ループ用の LOOP文の例を以下に記します。
ループカウンタを NUMBER で宣言し、ループ内でループカウンタをカウントアップし、
条件式でカウンタが5を超えた場合にループを抜ける様にしています。
EXIT 命令で END LOOP の下に制御が移ることが分かります。
無限ループ用の LOOP文の例
SQL> DECLARE
2 -- ループカウンタ
3 WK_CNT NUMBER(9);
4 BEGIN
5 -- カウンタ初期化
6 WK_CNT := 0;
7 -- 繰り返し処理
8 LOOP
9 -- カウンタ加算
10 WK_CNT := WK_CNT + 1;
11 -- カウンタが5を超えた場合はループを抜ける
12 IF WK_CNT > 5 THEN
13 EXIT;
14 END IF;
15 -- EXIT WHEN WK_CNT > 5; この方法でもOK
16
17 -- 繰り返し処理
18 DBMS_OUTPUT.PUT_LINE('...LOOP(' || TO_CHAR(WK_CNT) || ')回目の処理');
19 END LOOP;
20 -- EXITでここにくる
21 DBMS_OUTPUT.PUT_LINE('...LOOP終了');
22 END;
23 /
...LOOP(1)回目の処理
...LOOP(2)回目の処理
...LOOP(3)回目の処理
...LOOP(4)回目の処理
...LOOP(5)回目の処理
...LOOP終了
PL/SQLプロシージャが正常に完了しました。
SQL>
繰返しの条件付きループ用の WHILE...LOOP文の例
SQL> DECLARE
2 -- ループカウンタ
3 WK_CNT NUMBER(9);
4 BEGIN
5 -- カウンタ初期化
6 WK_CNT := 0;
7 -- 繰り返し処理
8 WHILE WK_CNT < 5 LOOP
9 -- カウンタ加算
10 WK_CNT := WK_CNT + 1;
11 -- 繰り返し処理
12 DBMS_OUTPUT.PUT_LINE('...LOOP(' || TO_CHAR(WK_CNT) || ')回目の処理');
13 END LOOP;
14 DBMS_OUTPUT.PUT_LINE('...LOOP終了');
15 END;
16 /
...LOOP(1)回目の処理
...LOOP(2)回目の処理
...LOOP(3)回目の処理
...LOOP(4)回目の処理
...LOOP(5)回目の処理
...LOOP終了
PL/SQLプロシージャが正常に完了しました。
SQL>
繰返し回数を指定してのループ用の FOR...LOOP文の例
SQL> BEGIN
2 -- 繰り返し処理
3 FOR I IN 1..5 LOOP
4 -- スキップ処理(偶数の場合)
5 CONTINUE WHEN (I MOD 2) = 0;
6 -- 繰り返し処理
7 DBMS_OUTPUT.PUT_LINE('...LOOP(' || I || ')回目の処理');
8 END LOOP;
9 DBMS_OUTPUT.PUT_LINE('...LOOP終了');
10 END;
11 /
...LOOP(1)回目の処理
...LOOP(3)回目の処理
...LOOP(5)回目の処理
...LOOP終了
PL/SQLプロシージャが正常に完了しました。
SQL>
ループカウンタ変数に関して通常の変数とは異なる以下の点があります。
- ループカウンタ変数を変数宣言部で宣言しなくても良い。宣言した数値変数を使用することはできます。
- ループ処理内ではループカウンタ変数には値を代入できない。
カーソルについて
PL/SQLの使い方において、テーブルからのデータを基にして各種の処理を行うことが重要な目的と思います。
テーブルからのデータを取得する方法の一つにカーソルがあります。
カーソルの考え方は、データを取得するSELECT文を定義し、そのSELECT文を解釈した後に順次1行(レコード)毎のデータを
取得し、最後の行までを取得した時点で処理を終えるというものです。
カーソルの処理の一連の命令を含んだ書式を以下に記します。
カーソルの処理の書式
DECLARE
-- カーソル定義
CURSOR <カーソル名> IS <問い合わせ文(SELECT文)>;
-- カーソル変数宣言
<カーソル変数名> <カーソル名>%ROWTYPE;
BEGIN
-- カーソルオープン
OPEN <カーソル名>;
-- カーソル処理のループ
LOOP
-- 1行のデータの取得
FETCH <カーソル名> INTO <カーソル変数名>;
-- カーソルのデータが無くなった??
IF <カーソル名>%NOTFOUND THEN
EXIT;
END IF;
-- 何かのデータ処理
END LOOP;
-- カーソルクローズ
CLOSE <カーソル名>;
END;
実際にこの書式に従ってプログラムした例を以下に記します。
カーソル処理の例
SQL> DECLARE
2 -- カーソル定義
3 CURSOR CUR_TEST IS SELECT ID, NAME FROM TEST ORDER BY ID;
4 -- カーソル変数宣言
5 CUR_WK CUR_TEST%ROWTYPE;
6 BEGIN
7 -- カーソルオープン
8 OPEN CUR_TEST;
9 -- カーソル処理のループ
10 LOOP
11 -- 1行のデータの取得
12 FETCH CUR_TEST INTO CUR_WK;
13 -- カーソルのデータが無くなった??
14 IF CUR_TEST%NOTFOUND THEN
15 EXIT;
16 END IF;
17 -- 何かのデータ処理
18 DBMS_OUTPUT.PUT_LINE('ID:' || CUR_WK.ID || ' NAME:' || CUR_WK.NAME);
19
20 END LOOP;
21 -- カーソルクローズ
22 CLOSE CUR_TEST;
23 END;
24 /
ID:1 NAME:aaa*****
ID:2 NAME:bbb@@@@@
ID:3 NAME:ccc+++++
ID:4 NAME:ddd-----
ID:9 NAME:
PL/SQLプロシージャが正常に完了しました。
SQL>
以下の様に FOR 文を使って処理することもできます。
この方法では、上記の様に自分自身でカーソルをオープンしフェッチ、及びクローズ処理が必要無くなりますので、
簡単な処理の場合にはこちらの方が便利だと思います。
FOR 文を使ったカーソル処理の例
SQL> DECLARE
2 -- カーソル定義
3 CURSOR CUR_TEST IS SELECT ID, NAME FROM TEST ORDER BY ID;
4 BEGIN
5 -- カーソル処理のループ
6 FOR CUR_WK IN CUR_TEST LOOP
7 -- 何かのデータ処理
8 DBMS_OUTPUT.PUT_LINE('ID:' || CUR_WK.ID || ' NAME:' || CUR_WK.NAME);
9 END LOOP;
10 END;
11 /
ID:1 NAME:aaa*****
ID:2 NAME:bbb@@@@@
ID:3 NAME:ccc+++++
ID:4 NAME:ddd-----
ID:9 NAME:
PL/SQLプロシージャが正常に完了しました。
SQL>
例外処理について(EXCEPTION)
PL/SQLのプログラムの重要な項目として例外処理があります。 プログラムの BEGIN ... END ブロックの中で発生した例外(プログラム実行が不可能な事由)に対応させるための処理です。 そのブロックの中に EXCEPTION 文を宣言し、各例外の種類に対応して例外の処理を記述します。 例外処理の一連の命令を含んだ書式を以下に記します。
例外処理の書式
BEGIN
<プログラム実行実体>;
...
EXCEPTION
WHEN <例外名1> THEN
<例外処理1>
WHEN <例外名2> THEN
<例外処理2>
WHEN <例外名n> THEN
<例外処理n>
END;
-- BEGIN ... END ブロックをネストした場合
BEGIN
<処理a>;
BEGIN
<処理>;
EXCEPTION
WHEN <例外名> THEN
<例外処理>
END;
<処理b>;
...
BEGIN
<処理>;
EXCEPTION
WHEN <例外名> THEN
<例外処理>
END;
...
EXCEPTION
WHEN <例外名> THEN
<例外処理>
...
END;
例外名はOracleのサイトからの引用ですが、以下のものがあります。尚、表の中で例外名が太字のものがよく使います。
| 例外名 | エラーコード | 備考 |
|---|---|---|
| ACCESS_INTO_NULL | -6530 | 初期化されていないオブジェクトの属性に値を割り当てようとしました。 |
| CASE_NOT_FOUND | -6592 | CASE文のWHEN句で何も選択されておらず、ELSE句もありません。 |
| COLLECTION_IS_NULL | -6531 | EXISTS以外のコレクション・メソッドを初期化されていないネストした表やVARRAYに適用しようとしたか、または初期化されていないネストした表やVARRAYの要素に値を割り当てようとしました。 |
| CURSOR_ALREADY_OPEN | -6511 | すでにオープンされているカーソルをオープンしようとしました。 |
| DUP_VAL_ON_INDEX | -1 | 一意索引によって制約されている列に重複値を挿入しようとしました。 |
| INVALID_CURSOR | -1001 | 無効なカーソル操作です。 |
| INVALID_NUMBER | -1722 | 文字列から数値への変換に失敗しました。 |
| LOGIN_DENIED | -1017 | ログインの失敗(拒否) |
| NO_DATA_FOUND | +100 | 単一行のSELECTによって行が戻されなかったか、またはプログラムによって、ネストした表内の削除された要素や連想配列(索引付き表)内の初期化されていない要素が参照されました。 |
| NO_DATA_NEEDED | -6548 | |
| NOT_LOGGED_ON | -1012 | データベースに接続せずにデータベース・コールが発行されました。 |
| PROGRAM_ERROR | -6501 | PL/SQLに内部的な問題が発生しました。 |
| ROWTYPE_MISMATCH | -6504 | 代入文のホスト・カーソル変数とPL/SQLカーソル変数の戻り型に互換性がありません。 |
| SELF_IS_NULL | -30625 | MEMBERメソッドを起動しようとしましたが、オブジェクトのインスタンスが初期化されていませんでした。 |
| STORAGE_ERROR | -6500 | PL/SQLのメモリーが不足しているか、メモリーが破損しています。 |
| SUBSCRIPT_BEYOND_COUNT | -6533 | コレクション内の要素の数より大きい索引番号を使用して、ネストした表またはVARRAYを参照しました。 |
| SUBSCRIPT_OUTSIDE_LIMIT | -6532 | 有効範囲外の索引番号(たとえば-1)を使用して、ネストした表またはVARRAYを参照しました。 |
| SYS_INVALID_ROWID | -1410 | 文字列が値ROWIDを表していないため、ユニバーサルROWIDへの文字列の変換に失敗しました。 |
| TIMEOUT_ON_RESOURCE | -51 | データベースでリソースを待機している間に、タイムアウトが発生しました。 |
| TOO_MANY_ROWS | -1422 | 単一行のSELECTによって、2つ以上の行が戻されました。 |
| VALUE_ERROR | -6502 | 算術、変換、切捨てまたはサイズ制限のエラーが発生しました。 |
| ZERO_DIVIDE | -1476 | 数値を0(ゼロ)で割ろうとしました。 |
| OTHERS | EXCEPTIONで指定した例外名以外のものを全てを表す。 |
簡単な例として、 ZERO_DIVIDE を使ったものを以下に示します。 この例に特に利用価値があるわけではありませんが、 EXCEPTION の使い方を見て下さい。
例外処理の例(ZERO_DIVIDE)
SQL> DECLARE
2 -- 数値変数宣言
3 WK NUMBER(9);
4 BEGIN
5 -- 強制的に0で割る
6 WK := 10 / 0;
7 DBMS_OUTPUT.PUT_LINE('正常終了');
8
9 EXCEPTION
10 WHEN ZERO_DIVIDE THEN
11 DBMS_OUTPUT.PUT_LINE('ZERO_DIVIDEエラーが発生!!');
12 WHEN OTHERS THEN
13 DBMS_OUTPUT.PUT_LINE('その他のエラーが発生!!');
14 END;
15 /
ZERO_DIVIDEエラーが発生!!
PL/SQLプロシージャが正常に完了しました。
SQL>
以下に NO_DATA_FOUND を使った例を示します。 FOR ループの中に BEGIN...END; がありますが、その中に例外処理を宣言しています。 TEST テーブルにはIDが 1 から 4 までしかないのでIDが 5 のSELECT文の実行時に例外が発生します。 EXCEPTION の中で NO_DATA_FOUND の場合の処理と、その他の例外である OTHERS の処理を行っています。
例外処理の例2(NO_DATA_FOUND)
SQL> DECLARE
2 /* ワーク変数の宣言 */
3 WK VARCHAR2(64);
4 BEGIN
5 -- 繰り返し処理
6 FOR I IN 1..5 LOOP
7 BEGIN
8 -- TESTテーブルからの取得
9 SELECT NAME INTO WK FROM TEST WHERE ID = I;
10 DBMS_OUTPUT.PUT_LINE('ID:' || I || ' NAME=' || WK);
11 EXCEPTION
12 -- エラーの場合
13 WHEN NO_DATA_FOUND THEN
14 DBMS_OUTPUT.PUT_LINE('ID:' || I || ' NO_DATA_FOUND のエラーが発生!!');
15 WHEN OTHERS THEN
16 DBMS_OUTPUT.PUT_LINE('ID:' || I || ' その他のエラーが発生!!');
17 END;
18 END LOOP;
19 DBMS_OUTPUT.PUT_LINE('...終了');
20 END;
21 /
ID:1 NAME=aaa*****
ID:2 NAME=bbb@@@@@
ID:3 NAME=ccc+++++
ID:4 NAME=ddd-----
ID:5 NO_DATA_FOUND のエラーが発生!!
...終了
PL/SQLプロシージャが正常に完了しました。
SQL>