数式を配置するプログラム

ラボ

数式を配置するプログラム(演習)

 上記のような集計表に数式マクロを配置していくVBAプログラムを演習形式で解説していきます。一見、簡単そうな処理に見えますが、休日をまたいで差分を計算しなければいけない点が少々厄介です。

プログラミング内容

 ある施設で電気・水道・ガスの使用量を集計する表があったとします。
施設が休みの時は、測定しないため、セルが塗りつぶされています。

 いつも月曜日が休み。というわけでもなくイレギュラーで連休などが入ります。このため数式マクロは規則的とはならず、仕方なく手作業で直していたというのが現状でした。

 VBAでは、数式マクロをセットする事もできますので、今回は休み(塗りつぶし)をセットしたらプログラムを実行して解決したいと思います。

数式マクロの式を直接指定

 まず、マクロですが、前日の指針値と当日の指針値との差を計算しています。
電気に関しては係数の2400を乗じる事になっているため *2400 が付いていますが、上水・ガスに関してはただの引き算になります。


前日が休みでない時は、以下のような式になります。

 =IF(D6=””,””,(D6-D5)*2400)

 当日が空白の時は、空白を返してマイナスの値を出さないようにしています。
もし、E6のセルのみであれば、文字列として上記の式を渡してもOKですが、”(ダブルクォーテーション)に関しては、エスケープシーケンスしなければいけないため、””と重ねる必要があります。例を挙げます。

Range(”E6″)=”=IF(D6=“,“,(D6-D5)*2400)”

やたらと冗長になりますが、このようになります。
青色の部分がエスケープシーケンス部分です。

Formula・FormulaR1C1

 上記の方法では、繰り返しのループに入れて使う事ができませんので、相対的に指定する必要があります。そこで登場するのがFormulaFormulaR1C1です。両者の違いは、
Formula はA1,B2などで絶対的指定
FormulaR1C1 はRCで相対的に指定する事ができる点です。

 今回は相対的に指定できるFormulaR1C1を使います。
FormulaR1C1は [ ] を付けると相対的な位置何もつけないと絶対的な位置を指定できます。
Rは行を指し、Cは列を指します。

Range("C3").FormulaR1C1 = "=RC1"        '=$A3

この例では、R(行)はそのまま、C1で一番目の列(A列)を指定しているのでA3の値を参照します。

Range("C5").FormulaR1C1 = "=RC[-1]"     '=B5

この例では、R(行)はそのまま、C[-1]で一つ手前の列(B列)を指定しているのでB5の値を参照します。

プログラムに組み込む

 では先の例をFormulaR1C1 で表現してみましょう。

Range(”E6″)=”=IF(D6=””””,””””,(D6-D5)*2400)”

これをFormulaR1C1 で表現すると、

Range(”E6″).FormulaR1C1 = “=IF(RC[-1]=””””,””””,(RC[-1]R[-1]C[-1])*2400)”

上記のようになりますね。これは、一つ前の指針値と引き算している例です。

今回のプログラムでは連休に対応したいので、赤色の部分を変数に置き換えます。
また、Range(”E6″)部分も、Cells(6,”E”)に置換えます。

ソースコード1

 プログラムを組むときは最初から全てを追わずに段階的に仕上げていく方が上手くいきます。
まずは、「使用量」に前日の値と当日の値を引き算した結果を表示するように書いてみます。

Sub setFormulaTest1()
    Dim i As Long
    
    For i = 6 To 36
        Cells(i, 5).FormulaR1C1 = "=IF(RC[-1]="""","""",(RC[-1]-R[-1]C[-1])*2400)" 
        Cells(i, 7).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-1]C[-1])" 
        Cells(i, 9).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-1]C[-1])" 
    Next i
End Sub

ソースコード2

 次の条件を追加します。網目のある行に関しては値が入りませんので、数式は不要ですので書き込まないようにします。
条件式は、
 If Cells(i, “D”).Interior.Pattern <> xlPatternSolid Then

となります。
Interior.Patternが網目のプロパティ設定で、
xlPatternSolidが「網目なし」の値となります。

網目なし以外(何らかの網目が設定されている時)は、For文の最後に飛ぶようにします。

ただ、これだけだと不十分です。B列の日付に注目しましょう。
小の月の場合は、空白が来る事があるので、これを捕まえる事にしましょう。

 If Cells(i, “D”).Interior.Pattern <> xlPatternSolid Or Cells(i, “B”) = “” Then GoTo LABEL

これで、数式を設定する箇所を特定できました。

Sub setFormulaTest2()
    Dim i As Long
    
    For i = 6 To 36
    
        'D列(値部分)のセルの網掛けが「無し」以外か、B列(日付)が空白でない場合
        If Cells(i, "D").Interior.Pattern <> xlPatternSolid Or Cells(i, "B") = "" Then GoTo LABEL
    
        Cells(i, 5).FormulaR1C1 = "=IF(RC[-1]="""","""",(RC[-1]-R[-1]C[-1])*2400)"
        Cells(i, 7).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-1]C[-1])"   
        Cells(i, 9).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-1]C[-1])"       
    
LABEL:
    Next i

End Sub

ソースコード3

 最後に、休日のずれを考慮するようにしましょう。

 Cells(i, 5).FormulaR1C1 = “=IF(RC[-1]=””””,””””,(RC[-1]-R[-1]C[-1])*2400)”

連休が重なるにつれて赤色の付いた1を増やさなければなりません。
連休は何日続くか不定ですが、今回はFor文で適当な最大値をセットする方法にします。

今回は月ごとの表なので、連休の想定される最大値は、月の日付の最大値31でOKでしょう。
二重のループになりますので、カウンタ変数の  を使います。
j は休日が何日続くかの値となります。

For文で、当日のセルから上に遡っていき、網目であれば上、また上と進み、IF文で網目の無い日を捕えます。

  If Cells(i – j, “D”).Interior.Pattern = xlPatternSolid Then

最終的に網目なしのセルに行き当たると、その値と当日の値との差を求める数式が記録されます。

Sub setFormula()
    Dim i As Long, j As Long
    
    For i = 6 To 36
        'D列(値部分)のセルの網掛けが「無し」以外か、B列(日付)が空白でない場合
        If Cells(i, "D").Interior.Pattern <> xlPatternSolid Or Cells(i, "B") = "" Then GoTo LABEL
        
        For j = 1 To 31 '連休間隔(勝手に抜けるので最大値は適当に大きな値)
            If Cells(i - j, "D").Interior.Pattern = xlPatternSolid Then
                Cells(i, 5).FormulaR1C1 = "=IF(RC[-1]="""","""",(RC[-1]-R[-" & j & "]C[-1])*2400)"   
                Cells(i, 7).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-" & j & "]C[-1])"   
                Cells(i, 9).FormulaR1C1 = "=IF(RC[-1]="""","""",RC[-1]-R[-" & j & "]C[-1])"   
                Exit For
            End If
        
        Next j
        
LABEL:
    Next i

End Sub

上手くいかない時は、、、

 これを書いている時、D5のセル部分で j のループで処理が止まらずにエラーとなるケースがありました。この状況を明示的に回避するためには冒頭で、

 Range(“D5”).Interior.Pattern = xlPatternSolid

としてやればOKです。D5セルは、前月の最終日の検針値を記載しているセルになります。
デバッガで探ったところ、網目が無い状態にもかかわらず、xlPatternSolid の値ではない状態でした。 おかしいと思った時はデバッガで辿ることが重要です。

コメント

タイトルとURLをコピーしました