SQLで提供されている制御構造は、Visual BasicやC++など一般的なプログラミング言語に比べて機能がシンプルなため、うまく整理しないとソースを読み取りにくくなる場合があります。条件判断と分岐の構造を読みやすくしてみましょう。
- 出口が多いと読みにくい -
前回の最後に「If~Else」の入れ子構造のサンプルとして、サーバー側のエラー発生時にエラー番号を返す@@ERROR関数を使う方法を紹介しました(ex01.sql~前回のex09.sqlと同じ内容です)。
Create Procedure YearlyTransfer3
As
SELECT * INTO 累積売上_old FROM 累積売上_dmy
If @@ERROR = 0 ----------- コピーが正常終了した場合
Begin
DELETE 累積売上_dmy ---- 削除処理を実行
If @@ERROR = 0 --------- 削除も正常終了したら
Begin ------------------ 処理終了
Print '処理は完了しました。'
Return 0 ------------- ★
End
Else ------------------- 削除がエラーの場合
Begin ------------------ メッセージを表示して終了
Print '削除操作でエラーが発生しました。'
Return @@ERROR ------- ★
End
End
Else --------------------- コピーがエラーの場合
Begin -------------------- メッセージを表示して終了
Print 'コピー操作でエラーが発生しました。'
Return @@ERROR --------- ★
End
★マークの箇所でReturn命令を使ってエラー番号(@@ERRORの戻り値)または0を返しています。しかし、1つのプロシージャ内にReturn命令が複数存在するのは、あまりよい構造とは言えません。
なぜなら、処理ブロック(この場合はプロシージャ)内に出口(プロシージャを抜ける命令)がたくさんあることで、処理の流れが読み取りにくくなるからです。
- 制御構造は見通しやすく -
Visual BasicのForやWhileで繰り返し構造(ループ)を作った場合を考えてみましょう。ループを抜けるための条件(脱出条件)があちこちに書かれていると、どのようなときにループを抜けて次の処理に移るのか見通しにくくなります。
次のリストはその典型例です。Forループの中にIfによる条件判定が複数あり、その中で変数などの状態を調べて(ループの繰り返し条件であるカウンタ変数の値とは無関係に)、あるときはループを抜け、またあるときはカウンタ変数の値を増加してループを繰り返す……という形で、どうにも美しい構造ではありません。
For intCount = 0 To __MAX_COUNT
........(処理)........
If chkLimitOn.Checked = True Then
Exit For ------------- ここで抜ける
Else
Next intCount -------- ここでは次のループへ
........(処理)........
If lngLineNum >= __MAX_LINE Then
Exit For ------------- ここでも抜ける
Else
Next intCount -------- ここでも次のループへ
........(処理)........
Next intCount ------------ 本当のループの終端
- 出口を1つにまとめる -
そこで、“YearlyTransfer3”の「エラーを判定してはその都度@@ERRORを呼び出し、Return命令でエラーコードを返す」という形を見直し、出口を1つにしてみましょう。@@ERROR関数の返す値は直前に実行された命令の結果ですから、変数を用意してエラー番号を保存し、処理の最後でその値を返すようにします。
変数への代入処理が増えるためソースは長くなりますが、流れは見通しやすくなります(ex02.sql)。
Create Procedure YearlyTransfer4
As
DECLARE @ErrCode int ---------- エラー番号を保存する変数を宣言
SELECT * INTO 累積売上_old FROM 累積売上_dmy
If @@ERROR = 0
Begin
DELETE 累積売上_dmy
If @@ERROR = 0
Begin
Print '処理は完了しました。'
Set @ErrCode = 0 ---------- エラー番号を変数に保存
End
Else
Begin
Print '削除操作でエラーが発生しました。'
Set @ErrCode = @@ERROR ---- エラー番号を変数に保存
End
End
Else
Begin
Print 'コピー操作でエラーが発生しました。'
Set @ErrCode = @@ERROR ------ エラー番号を変数に保存
End
Return @ErrCode ----------- 最後に変数の値(エラー番号)を返す
- コメント記号 -
さて、ここまでソースコードが長くなってくると、どの行で何をしているのかが把握しづらくなります。そんなときはコメントを使い、各行で何をしているかをコメントとして記述しておくと便利です。
コメントには--または/* */を使います。--は1行コメントで、--から行末(改行)までは、何を書いても処理対象とみなされません。
複数行のコメントは/*と*/で囲みます。--はSQL-92準拠のコメント記号ですが、/*~*/はTRANSACT-SQL独自のコメント記号です。
コメントを挿入すると、先に紹介した“YearlyTransfer4”は以下のようになります(ex03.sql)。クエリアナライザでは、SQLキーワード、関数、文字列、コメントがそれぞれ色分けされて表示されるため、コメント記号の入力を間違えるとすぐに分かります。
Create Procedure YearlyTransfer5
As
/*
途中でエラーが発生したら、メッセージを表示して
エラー番号を返す。
*/
-- エラー番号を保存する変数を宣言
DECLARE @ErrCode int
SELECT * INTO 累積売上_old FROM 累積売上_dmy
If @@ERROR = 0
Begin
DELETE 累積売上_dmy
If @@ERROR = 0
Begin
Print '処理は完了しました。'
Set @ErrCode = 0 -- エラー番号を変数に保存
End
Else
Begin
Print '削除操作でエラーが発生しました。'
Set @ErrCode = @@ERROR -- エラー番号を変数に保存
End
End
Else
Begin
Print 'コピー操作でエラーが発生しました。'
Set @ErrCode = @@ERROR -- エラー番号を変数に保存
End
-- 最後に変数の値(エラー番号)を返す
Return @ErrCode
|