PL/SQLの勉強14 例外処理の書き方
今回は「エラー」についてやっていきます。
プログラムを書いていると、必ず「思い通りにはいかない」時がやってきます。
例えば「想定外のデータが入力された」とか、「テーブルにあると思ってたデータが無かった」とか、逆に「想定以上にデータ数が多い」とか。。。
全ての例外を想定するのは難しいですが、プログラムを書くものとして、ある程度は『想定して』『対応して』おきましょう。
例外処理の書き方(基本型)
まずは基本型からです。
四則計算の基本的な、やってはいけない「0割り」を使ってエラーを出してみようと思います。
こう書きます。
declare
n_num number;
begin
-- 0割り:必ずエラーになる!
n_num := 1/0;
-- 数字を出力しようとする
dbms_output.put_line(n_num);
exception
when others then
dbms_output.put_line('エラー');
end;
5行目で0割りとなって「exception」に飛びました。
ので、7行目に数字を出力しようとしても表示されていません。
なぜエラーとなったか?の情報を取得する
上記の例だと「0割りだと分かって」書いています。
でも実際は、どこにエラー(想定外のこと)があるんだろう?と探す必要があります。
なぜエラーになったのか?なぜexceptionに飛んだのか?を知るために、oracleは基本的な情報を提供しています。それを知る術を書いてみます。
declare
n_num number;
begin
-- 0割り:必ずエラーになる!
n_num := 1/0;
-- 数字を出力しようとする
dbms_output.put_line(n_num);
exception
when others then
dbms_output.put_line('エラー発生!');
dbms_output.put_line('sqlcode : ' || sqlcode);
dbms_output.put_line('sqlerrm : ' || sqlerrm);
end;
11行目・12行目を追加しました。この2つが、エラーの内容を知るのに必要な情報です。
sqlcode
エラーを番号で種類分けされています。
今回は「-1476番のエラー」ということになります。つまり0割りは1476番、ということです。
「oracle エラー 1476」で検索すれば、エラーの内容がなんとなく分かります。
sqlerrm
エラーの内容を、oracleが用意したメッセージで返してくれます。
今回は「divisor is equal to zero」と書いてありますので、0で割っているのが原因と分かります。出力されたエラーメッセージがよく分からないときは、エラーメッセージで検索するとヒントが得られるはずです。
例外処理の書き方(詳細)
エラーとなったとき、何も考えずに以下のように書いていました。
when others then
「others」と書いています。「他のエラーがあったとき」という意味です。
つまり、PL/SQLには必要最低限のエラーハンドリングが用意されています。それ以外の時に「others」を使います。
0割りをハンドリングする
例えば以下のように書きます。
declare
n_num number;
begin
-- 0割り:必ずエラーになる!
n_num := 1/0;
-- 数字を出力しようとする
dbms_output.put_line(n_num);
exception
when zero_divide then
dbms_output.put_line('0割りしてます!');
when others then
dbms_output.put_line('エラー発生!');
dbms_output.put_line('sqlcode : ' || sqlcode);
dbms_output.put_line('sqlerrm : ' || sqlerrm);
end;
9行目〜10行目を追加しました。
「zero_divide」つまり「0割りのときは…」という意味で使える例外処理です。
「zero_divide」の時に用意したメッセージが出力されています。
0割りでない例外を見る(othersに行く場合)
例えば0割りでなく、四則計算に文字があった場合…以下のようになります。
declare
n_num number;
begin
-- 数式に文字列:必ずエラーになる!
n_num := 1/'a';
-- 数字を出力しようとする
dbms_output.put_line(n_num);
exception
when zero_divide then
dbms_output.put_line('0割りしてます!');
when others then
dbms_output.put_line('エラー発生!');
dbms_output.put_line('sqlcode : ' || sqlcode);
dbms_output.put_line('sqlerrm : ' || sqlerrm);
end;
5行目で「n_num := 1/’a’;」としているので、違うエラーとなります。その結果が「others」を通ってエラーの内容を出力しています。
想定していなかった例外が起きた時に「others」を使うようにします。逆に、事前に想定されるエラーは「others」を使わないようにするべきです。
今回の例だと、以下のように書くことが出来ます。
0割りでない例外を見る(想定される例外をハンドリング)
declare
n_num number;
begin
-- 数式に文字列:必ずエラーになる!
n_num := 1/'a';
-- 数字を出力しようとする
dbms_output.put_line(n_num);
exception
when zero_divide then
dbms_output.put_line('0割りしてます!');
when value_error then
dbms_output.put_line('文字列で計算してます!');
when others then
dbms_output.put_line('エラー発生!');
dbms_output.put_line('sqlcode : ' || sqlcode);
dbms_output.put_line('sqlerrm : ' || sqlerrm);
end;
PL/SQLで「value_error」という例外が用意されていますので、それを使います。
上記の例では「0割り」と「文字列で計算」の2つを想定して例外処理を作り、他は全くの想定外として「others」でハンドリングしています。
よく使う例外処理
私が個人的に、よく使う例外処理があります。
上では0割りについてやっていきましたが、正直あまり使いません(全く使わない、といったわけではなく)。
私がよく使うのは「テーブルから1件のデータを取得したい時」です。
例えばテーブル「hoge_table」に以下のデータが入っていたとします。
ID | NAME |
---|---|
1 | 一郎 |
2 | 二郎 |
2 | 次郎 |
3 | 三郎 |
こんな感じな例外をよく書きます。
too_many_rows
declare
v_name HOGE_TABLE.NAME%type;
begin
select NAME
into v_name
from HOGE_TABLE
where ID = 2
;
dbms_output.put_line('NAME : ' || v_name);
exception
when no_data_found then
dbms_output.put_line('データが1件もありません!');
when too_many_rows then
dbms_output.put_line('データが複数件あります!');
when others then
dbms_output.put_line('エラー');
end;
7行目で「ID = 2」と条件を指定しています。
ID = 2なのは「二郎」「次郎」の2データあるので、エラーとなります。
結果は以下のようになります。
no_data_found
declare
v_name HOGE_TABLE.NAME%type;
begin
select NAME
into v_name
from HOGE_TABLE
where ID = 4
;
dbms_output.put_line('NAME : ' || v_name);
exception
when no_data_found then
dbms_output.put_line('データが1件もありません!');
when too_many_rows then
dbms_output.put_line('データが複数件あります!');
when others then
dbms_output.put_line('エラー');
end;
7行目で「ID = 4」と条件を指定しています。
ID = 4の場合はデータが無いので、エラーとなります。
結果は以下のようになります。
その他の例外名
PL/SQLにはその他にも色々な例外が用意されています。
2023年6月時点では、以下の例外名が存在します。
ACCESS_INTO_NULL
CASE_NOT_FOUND
COLLECTION_IS_NULL
CURSOR_ALREADY_OPENED
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALID_NUMBER
NO_DATA_FOUND
PROGRAM_ERROR
ROWTYPE_MISMATCH
STORAGE_ERROR
SUBSCRIPT_BEYOND_COUNT
SUBSCRIPT_OUTSIDE_LIMIT
SYS_INVALID_ROWID
TOO_MANY_ROWS
VALUE_ERROR
ZERO_DIVIDE
詳しくはoracleが用意しているリファレンスを見てください。
私もこの全てを覚えているわけではありません。プログラムを書く度に、使えそうな例外名が無いか?調べて確認する程度です。
以上ですw
想定していないデータがあったので、処理が落ちました。
というのは、ちょくちょくある話です。なのですが、だからと言って「処理が落ちても仕方ない」とはなりません。
- なぜ処理が落ちたのか?
- どこに原因があるのか?
を探るには、それなりの情報をログなどに出力しておく必要があります。そのために、今回やった例外処理をうまく活用してほしい、と思います。
Discussion
New Comments
No comments yet. Be the first one!