VBAのプログラムの実行時にエラーが発生した場合、もしその発生行のコードがシンプルであればすぐにその要因に気付くこともできるのですが、長い式が書き連ねられている場合、その中のどこに要因があってどこを修正すればよいか分からないこともあります。
そのようなとき、適当に怪しいところを書き換えて再実行して試行錯誤でエラーを潰していく方法もありますが、やみくもにやっても時間のムダとなってしまいます。
そのひとつの対応策として、式内に含まれる一部の関数式を取り出してその結果を事前に変数にセットしておくことで、式を単純化する方法があります。それによってそれぞれの関数式に問題がないかのチェックが容易になります。
(例)
Result = Int(X) + Int(Y)
A = Int(X)
B = Int(Y)
Result = A + B
一方ここでは別の方法として、長い式自体は変えることなく、VBAのデバッグ手法、特にイミディエイトウィンドウを活用することでエラー要因を調べ修正していく方法について説明します。
その例として、下記のようなコードがあるとします。
Dim dbs As Database
Dim rst As Recordset
Dim intMM As Integer
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset("tbl販売実績集計")
With rst
Do Until .EOF
For intMM = 1 To 6
Debug.Print Format$(Int((CInt(.Fields(intMM + 6 & "月")) - CInt(.Fields(intMM & "月"))) / 10) * 10, "#,###ケ")
Next intMM
.MoveNext
Loop
.Close
End With
ここでは、テーブル「tbl販売実績集計」のレコードを取り出して、「1月と7月の差異」、「2月と8月の差異」・・・・というように、前半と後半の各月の値の差を求めています。
「Debug.Print」の行が”長い式”となっていますが、ここでは次のような処理をしています。・・・これ自体はあまり実際的でありませんがあくまでも例ということで見てください
- まず、intMM変数で表わされる月(ループの最初は「1月」)のフィールドに保存されている値を取得します → .Fields(intMM & "月")
- その値はフィールド構造上”小数”で保存されている(こととしている)ので、CInt関数を使って小数点以下を四捨五入して”整数”化します
- その6ヶ月後の月(ループの最初は「7月」)フィールドに保存されている値を取得します → .Fields(intMM + 6 & "月")
- 同様に整数化します
- 「7月の値 - 1月の値」を計算して差を求めます
- その結果について、Int関数を利用することで1の位を切り捨てて、”10単位”に丸めます
- 最後にその値を書式化して「1,234ケ」といったような形式で出力します
このコードを実行すると、下図のようなエラーが発生します。
プログラミングに慣れてくるとこの時点でテーブルに保存されている値がNull値(ゼロではなく空)であることが原因と何となく分かるのですが、実際に書くさまざまなコードではこれ以外にも多くのエラーメッセージが表示されることもありますので、ここではそれが漠然としたものという前提で説明します。
それでは、このようなケースでのデバッグ方法について説明していきます。基本的には、イミディエイトウィンドウを使って、式内の部分部分の値を対話的に検証し、解決方法を模索していくというやり方になります。
- まずプログラムを実行し、エラーが発生して(ここではエラーメッセージが表示されて)、プログラムがストップした状態にします。
- エラーメッセージダイアログにある[デバッグ]ボタンをクリックします。
それによって画面がコードのウィンドウ(VBE:Visual Basic Editor)に切り替わるとともに、エラーが発生している行全体の背景色が”黄色”になっていることが分かります。
以降はその状態、つまりプログラムがエラー終了した状態ではなく「その行を実行しようとしているが出来ずに一時中断している」状態でデバッグ作業を行います。
- まず、VBEのメニューから[表示]-[イミディエイトウィンドウ]を選択するか、ショートカットキー「Ctrl+G」を押して、イミディエイトウィンドウを開きます。
- このようなデバッグでは、長い式全体を確認しても当然同じ結果になりますので、まずはあるひとつの変数などの細かい要素の状態を確認し、順次それをひとつの関数式さらには複数の関数式というように広げていくことがポイントとなります。
そこでまず、ここでは「intMM」変数の値を確認します。
それには、イミディエイトウィンドウに「?intMM」と入力してEnterキーを押します。それによってintMMの値が現在「4」、つまりループの4月の時点であることが確認できます。エラーもその値自体では起こりません。
- イミディエイトウィンドウでは、「?(クエスチョンマーク)」に続いて変数名や関数式などをキーインしてEnterキーを押すことで、その値が次の行に表示され、対話式でそれらの結果を知ることができます。
- このような作業では、直接「intMM」とキーインしてもよいですが、コードウィンドウからそれらの文字列を範囲選択してコピー&ペーストした方がより確実です(キーインした文字が間違っていたら元も子もありませんので)。
- 次に「?.Fields(intMM & "月")」、「?.Fields(intMM + 6 & "月")」それぞれをイミディエイトウィンドウに入力してEnterキーを押します。それによってカレントレコードのフィールドの値がどうなっているかを調べることができます。
その結果、前者の値が「Null」であることが分かります。
- 次に、「?CInt(.Fields(intMM & "月"))」、「?CInt(.Fields(intMM + 6 & "月"))」それぞれをイミディエイトウィンドウに入力してEnterキーを押します。
その結果、前者ではプログラム全体を実行したときと同じエラーが発生することが分かります。
このことから、『CInt関数にNullの値が引数として与える際にエラーが発生していた』と推測できます。
- それを確認するため、今度は「?CInt(Null)」を実行してみます。
同様のエラーとなりましたので、この部分に修正を加えればよいと判断できます。
- この時点でコードの修正を行ってもよいですが、まずはイミディエイトウィンドウを使って、その修正を行うことでその行全体の式が改善されるかを確認しておくことにします。
フィールドを参照する部分を適当な値に書き換えて、「?Format$(Int((CInt(1234.78) - CInt(234)) / 10) * 10, "#,###ケ")」という式を実行してみます。
エラーが起こらずに正しい値が表示されていますので、上記の箇所を修正すればその行全体が正常動作するだろうということが分かります。
?CInt(1234.78), CInt(234)
?CInt(1234.78) - CInt(234)
?Int(CInt(1234.78) - CInt(234)) / 10
?Int((CInt(1234.78) - CInt(234)) / 10) * 10
?Format$(Int((CInt(1234.78) - CInt(234)) / 10) * 10, "#,###ケ")
- 今回場合はNull値が問題なので、「Nz関数」を使うことで、「フィールドの値がNullだったらゼロとして計算させる」ようにコードを改良します。
現在プログラムは中断している状態になっていますので、VBEのメニューから[実行]-[リセット]を選択するかツールバーの[■]ボタンをクリックしてプログラムが終了した状態に戻したあと、コードを下記のように書き換えます。
Debug.Print Format$(Int((CInt(.Fields(intMM + 6 & "月")) - CInt(.Fields(intMM & "月"))) / 10) * 10, "#,###ケ")
Debug.Print Format$(Int((CInt( Nz(.Fields(intMM + 6 & "月"))) - CInt( Nz(.Fields(intMM & "月")))) / 10) * 10, "#,###ケ")
- プログラム全体を再実行し、動作確認を行います。もし他のエラーが発生するようなことがあれば、上記の作業を繰り返すことでデバッグを行っていきます。
|